Merge lp:~thomas-voss/dbus-cpp/fix-race-and-add-tests into lp:~phablet-team/dbus-cpp/soname_bump

Proposed by Thomas Voß
Status: Superseded
Proposed branch: lp:~thomas-voss/dbus-cpp/fix-race-and-add-tests
Merge into: lp:~phablet-team/dbus-cpp/soname_bump
Diff against target: 1062 lines (+298/-481)
13 files modified
include/core/dbus/impl/object.h (+18/-31)
include/core/dbus/object.h (+12/-1)
include/core/dbus/pending_call.h (+0/-6)
src/core/dbus/asio/executor.cpp (+36/-25)
src/core/dbus/asio_executor.cpp (+0/-376)
src/core/dbus/bus.cpp (+11/-1)
src/core/dbus/dbus.cpp (+1/-0)
src/core/dbus/pending_call_impl.h (+56/-30)
tests/bus_test.cpp (+19/-4)
tests/dbus_test.cpp (+7/-3)
tests/executor_test.cpp (+135/-0)
tests/signal_delivery_test.cpp (+2/-2)
tests/test_service.h (+1/-2)
To merge this branch: bzr merge lp:~thomas-voss/dbus-cpp/fix-race-and-add-tests
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+209381@code.launchpad.net

This proposal supersedes a proposal from 2014-03-05.

This proposal has been superseded by a proposal from 2014-03-05.

Description of the change

Add a test for racyness and provide an alternative to _and_block.

To post a comment you must log in.
51. By Thomas Voß

Remove obsolete std::cout's.

52. By Thomas Voß

Cleanup whitespace.

53. By Thomas Voß

Remove obsolete std::this_thread::sleep_for in tests/bus_test.cpp.

54. By Thomas Voß

Fix racyness in signal-delivery test.

55. By Thomas Voß

Add missing symbols for coverage build.

56. By Thomas Voß

Add a linker script to force the linker to only export symbols that are in namespace core::dbus::*.
Adjust symbol files accordingly.

57. By Thomas Voß

Adjust symbol file for 32bit platforms to account for difference in size of integer types.

58. By Thomas Voß

Add specific symbols file for coverage builds, only containing optional symbols.

59. By Thomas Voß

Add missing coverage-specific symbols for armhf.

60. By Thomas Voß

Make sure that -fstack-protector is stripped from the compiler flags to avoid build failures on arm64.

61. By Thomas Voß

Move stripping of -fstack-protector before include.

Unmerged revisions

61. By Thomas Voß

Move stripping of -fstack-protector before include.

60. By Thomas Voß

Make sure that -fstack-protector is stripped from the compiler flags to avoid build failures on arm64.

59. By Thomas Voß

Add missing coverage-specific symbols for armhf.

58. By Thomas Voß

Add specific symbols file for coverage builds, only containing optional symbols.

57. By Thomas Voß

Adjust symbol file for 32bit platforms to account for difference in size of integer types.

56. By Thomas Voß

Add a linker script to force the linker to only export symbols that are in namespace core::dbus::*.
Adjust symbol files accordingly.

55. By Thomas Voß

Add missing symbols for coverage build.

54. By Thomas Voß

Fix racyness in signal-delivery test.

53. By Thomas Voß

Remove obsolete std::this_thread::sleep_for in tests/bus_test.cpp.

52. By Thomas Voß

Cleanup whitespace.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/core/dbus/impl/object.h'
2--- include/core/dbus/impl/object.h 2014-01-24 11:23:09 +0000
3+++ include/core/dbus/impl/object.h 2014-03-05 07:16:59 +0000
4@@ -85,6 +85,12 @@
5 }
6
7 template<typename Method, typename ResultType, typename... Args>
8+inline Result<ResultType> Object::transact_method(const Args& ... args)
9+{
10+ return invoke_method_asynchronously<Method, ResultType, Args...>(args...).get();
11+}
12+
13+template<typename Method, typename ResultType, typename... Args>
14 inline std::future<Result<ResultType>> Object::invoke_method_asynchronously(const Args& ... args)
15 {
16 auto msg_factory = parent->get_connection()->message_factory();
17@@ -97,41 +103,22 @@
18 if (!msg)
19 throw std::runtime_error("No memory available to allocate DBus message");
20
21- msg->writer().append(args...);
22+ auto writer = msg->writer();
23+ encode_message(writer, args...);
24
25 auto pending_call =
26 parent->get_connection()->send_with_reply_and_timeout(
27- msg->get(), Method::default_timeout());
28+ msg, Method::default_timeout());
29
30- auto cb =
31- [](DBusPendingCall* pending, void* user_data)
32- {
33- auto promise = static_cast<std::promise<Result<ResultType>>*>(user_data);
34-
35- auto msg = dbus_pending_call_steal_reply(pending);
36- if (msg)
37- {
38- auto result = Result<ResultType>::from_message(msg);
39- promise->set_value(result);
40- }
41- else
42- {
43- promise->set_exception(std::make_exception_ptr(std::runtime_error("Method invocation timed out")));
44- }
45- };
46-
47- auto promise = new std::promise<Result<ResultType>>();
48-
49- dbus_pending_call_set_notify(
50- pending_call,
51- cb,
52- promise,
53- [](void* p)
54- {
55- delete static_cast<std::promise<Result<ResultType>>*>(p);
56- });
57-
58- return promise->get_future();
59+ auto promise = std::make_shared<std::promise<Result<ResultType>>>();
60+ auto future = promise->get_future();
61+
62+ pending_call->then([promise](const Message::Ptr& reply)
63+ {
64+ promise->set_value(Result<ResultType>::from_message(reply));
65+ });
66+
67+ return future;
68 }
69
70 template<typename PropertyDescription>
71
72=== modified file 'include/core/dbus/object.h'
73--- include/core/dbus/object.h 2014-01-24 11:17:26 +0000
74+++ include/core/dbus/object.h 2014-03-05 07:16:59 +0000
75@@ -97,7 +97,7 @@
76 inline void emit_signal(const Args& ... args);
77
78 /**
79- * @brief Invokes a method of a remote object blocking while waiting for the result.
80+ * @brief Invokes a method of a remote object blocking the bus instance while waiting for the result.
81 * @tparam Method The method to invoke.
82 * @tparam ResultType The expected type of the result.
83 * @tparam Args Parameter pack of arguments passed to the invocation.
84@@ -108,6 +108,17 @@
85 inline Result<ResultType> invoke_method_synchronously(const Args& ... args);
86
87 /**
88+ * @brief Invokes a method of a remote object blocking the current thread while waiting for the result.
89+ * @tparam Method The method to invoke.
90+ * @tparam ResultType The expected type of the result.
91+ * @tparam Args Parameter pack of arguments passed to the invocation.
92+ * @param [in] args Argument instances passed to the invocation.
93+ * @return A future wrapping an invocation result, either signalling an error or containing the result of the invocation.
94+ */
95+ template<typename Method, typename ResultType, typename... Args>
96+ inline Result<ResultType> transact_method(const Args& ... args);
97+
98+ /**
99 * @brief Invokes a method of a remote object returning a std::future to synchronize with the result
100 * @tparam Method The method to invoke.
101 * @tparam ResultType The expected type of the result.
102
103=== modified file 'include/core/dbus/pending_call.h'
104--- include/core/dbus/pending_call.h 2013-11-27 18:57:42 +0000
105+++ include/core/dbus/pending_call.h 2014-03-05 07:16:59 +0000
106@@ -63,12 +63,6 @@
107 bool operator==(const PendingCall&) const = delete;
108
109 /**
110- * @brief Suspends the current thread until a reply has arrived.
111- * @return Pointer to a message.
112- */
113- virtual std::shared_ptr<Message> wait_for_reply() = 0;
114-
115- /**
116 * @brief Cancels the outstanding call.
117 */
118 virtual void cancel() = 0;
119
120=== modified file 'src/core/dbus/asio/executor.cpp'
121--- src/core/dbus/asio/executor.cpp 2013-11-27 18:57:42 +0000
122+++ src/core/dbus/asio/executor.cpp 2014-03-05 07:16:59 +0000
123@@ -25,7 +25,12 @@
124 #include <boost/asio/io_service.hpp>
125
126 #include <stdexcept>
127+
128+#include <condition_variable>
129 #include <memory>
130+#include <mutex>
131+#include <future>
132+#include <thread>
133
134 namespace core
135 {
136@@ -40,7 +45,7 @@
137
138 static inline bool is_timeout_enabled(DBusTimeout* timeout)
139 {
140- return dbus_timeout_get_enabled(timeout);
141+ return TRUE == dbus_timeout_get_enabled(timeout);
142 }
143
144 static inline int get_timeout_interval(DBusTimeout* timeout)
145@@ -64,7 +69,7 @@
146
147 static inline bool is_watch_enabled(DBusWatch* watch)
148 {
149- return dbus_watch_get_enabled(watch);
150+ return TRUE == dbus_watch_get_enabled(watch);
151 }
152
153 static inline int get_watch_unix_fd(DBusWatch* watch)
154@@ -105,9 +110,9 @@
155 throw std::runtime_error("Precondition violated: timeout has to be non-null");
156 }
157
158- ~Timeout() noexcept
159+ ~Timeout()
160 {
161- timer.cancel();
162+ // cancel();
163 }
164
165 void start()
166@@ -117,37 +122,36 @@
167 return;
168 }
169
170- timer.expires_from_now(boost::posix_time::milliseconds(traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(timeout)));
171- timer.async_wait(std::bind(&Timeout::on_timeout, Timeout<UnderlyingTimeoutType>::shared_from_this(), std::placeholders::_1));
172+ timer.expires_from_now(
173+ boost::posix_time::milliseconds(
174+ traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(
175+ timeout)));
176+ timer.async_wait(
177+ std::bind(&Timeout::on_timeout,
178+ Timeout<UnderlyingTimeoutType>::shared_from_this(),
179+ std::placeholders::_1));
180 }
181
182- void restart_or_cancel()
183+ void cancel()
184 {
185- if (!traits::Timeout<UnderlyingTimeoutType>::is_timeout_enabled(timeout))
186+ try
187 {
188 timer.cancel();
189- return;
190+ } catch(...)
191+ {
192+ // Really not sure what we should do about exceptions here.
193 }
194-
195- timer.expires_from_now(boost::posix_time::milliseconds(traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(timeout)));
196- timer.async_wait(std::bind(&Timeout::on_timeout, Timeout<UnderlyingTimeoutType>::shared_from_this(), std::placeholders::_1));
197- }
198-
199- void cancel()
200- {
201- timer.cancel();
202 }
203
204 void on_timeout(const boost::system::error_code& ec)
205 {
206+ if (ec == boost::asio::error::operation_aborted)
207+ return;
208+
209 if (ec)
210 return;
211
212- if (ec != boost::asio::error::operation_aborted)
213- {
214- traits::Timeout<UnderlyingTimeoutType>::invoke_timeout_handler(timeout);
215- restart_or_cancel();
216- }
217+ traits::Timeout<UnderlyingTimeoutType>::invoke_timeout_handler(timeout);
218 }
219
220 boost::asio::io_service& io_service;
221@@ -299,7 +303,7 @@
222 Holder<std::shared_ptr<Timeout<>>>::ptr_delete);
223
224 t->start();
225- return true;
226+ return TRUE;
227 }
228
229 static void on_dbus_remove_timeout(DBusTimeout* timeout, void*)
230@@ -310,13 +314,20 @@
231 static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*)
232 {
233 auto holder = static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout));
234- holder->value->restart_or_cancel();
235+ holder->value->start();
236 }
237
238 static void on_dbus_wakeup_event_loop(void* data)
239 {
240 auto thiz = static_cast<Executor*>(data);
241- thiz->io_service.post(std::bind(dbus_connection_dispatch, thiz->bus->raw()));
242+ auto bus = thiz->bus;
243+ thiz->io_service.post([bus]()
244+ {
245+ while (dbus_connection_get_dispatch_status(bus->raw()) == DBUS_DISPATCH_DATA_REMAINS)
246+ {
247+ dbus_connection_dispatch(bus->raw());
248+ }
249+ });
250 }
251
252 public:
253
254=== removed file 'src/core/dbus/asio_executor.cpp'
255--- src/core/dbus/asio_executor.cpp 2013-11-27 18:57:42 +0000
256+++ src/core/dbus/asio_executor.cpp 1970-01-01 00:00:00 +0000
257@@ -1,376 +0,0 @@
258-/*
259- * Copyright © 2012 Canonical Ltd.
260- *
261- * This program is free software: you can redistribute it and/or modify it
262- * under the terms of the GNU Lesser General Public License version 3,
263- * as published by the Free Software Foundation.
264- *
265- * This program is distributed in the hope that it will be useful,
266- * but WITHOUT ANY WARRANTY; without even the implied warranty of
267- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
268- * GNU Lesser General Public License for more details.
269- *
270- * You should have received a copy of the GNU Lesser General Public License
271- * along with this program. If not, see <http://www.gnu.org/licenses/>.
272- *
273- * Authored by: Thomas Voß <thomas.voss@canonical.com>
274- */
275-
276-#include <core/dbus/bus.h>
277-#include <core/dbus/executor.h>
278-#include <core/dbus/traits/timeout.h>
279-#include <core/dbus/traits/watch.h>
280-
281-#include <boost/asio.hpp>
282-#include <boost/asio/io_service.hpp>
283-
284-#include <stdexcept>
285-#include <memory>
286-
287-namespace core
288-{
289-namespace dbus
290-{
291-namespace traits
292-{
293-template<>
294-struct Timeout<DBusTimeout>
295-{
296- typedef int DurationType;
297-
298- static inline bool is_timeout_enabled(DBusTimeout* timeout)
299- {
300- return dbus_timeout_get_enabled(timeout);
301- }
302-
303- static inline int get_timeout_interval(DBusTimeout* timeout)
304- {
305- return DurationType(dbus_timeout_get_interval(timeout));
306- }
307-
308- static inline void invoke_timeout_handler(DBusTimeout* timeout)
309- {
310- dbus_timeout_handle(timeout);
311- }
312-};
313-
314-template<>
315-struct Watch<DBusWatch>
316-{
317- inline static int readable_event() { return DBUS_WATCH_READABLE; }
318- inline static int writeable_event() { return DBUS_WATCH_WRITABLE; }
319- inline static int error_event() { return DBUS_WATCH_ERROR; }
320- inline static int hangup_event() { return DBUS_WATCH_HANGUP; }
321-
322- static inline bool is_watch_enabled(DBusWatch* watch)
323- {
324- return dbus_watch_get_enabled(watch);
325- }
326-
327- static inline int get_watch_unix_fd(DBusWatch* watch)
328- {
329- return dbus_watch_get_unix_fd(watch);
330- }
331-
332- static inline bool is_watch_monitoring_fd_for_readable(DBusWatch* watch)
333- {
334- return dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE;
335- }
336-
337- static bool is_watch_monitoring_fd_for_writable(DBusWatch* watch)
338- {
339- return dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE;
340- }
341-
342- static bool invoke_watch_handler_for_event(DBusWatch* watch, int event)
343- {
344- return dbus_watch_handle(watch, event);
345- }
346-};
347-}
348-namespace asio
349-{
350-class Executor : public core::dbus::Executor
351-{
352- public:
353- template<typename UnderlyingTimeoutType = DBusTimeout>
354- struct Timeout : std::enable_shared_from_this<Timeout<UnderlyingTimeoutType>>
355- {
356- Timeout(boost::asio::io_service& io_service, UnderlyingTimeoutType* timeout)
357- : io_service(io_service),
358- timer(io_service),
359- timeout(timeout)
360- {
361- if (!timeout)
362- throw std::runtime_error("Precondition violated: timeout has to be non-null");
363- }
364-
365- ~Timeout() noexcept
366- {
367- timer.cancel();
368- }
369-
370- void start()
371- {
372- if (!traits::Timeout<UnderlyingTimeoutType>::is_timeout_enabled(timeout))
373- {
374- return;
375- }
376-
377- timer.expires_from_now(boost::posix_time::milliseconds(traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(timeout)));
378- timer.async_wait(std::bind(&Timeout::on_timeout, Timeout<UnderlyingTimeoutType>::shared_from_this(), std::placeholders::_1));
379- }
380-
381- void restart_or_cancel()
382- {
383- if (!traits::Timeout<UnderlyingTimeoutType>::is_timeout_enabled(timeout))
384- {
385- timer.cancel();
386- return;
387- }
388-
389- timer.expires_from_now(boost::posix_time::milliseconds(traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(timeout)));
390- timer.async_wait(std::bind(&Timeout::on_timeout, Timeout<UnderlyingTimeoutType>::shared_from_this(), std::placeholders::_1));
391- }
392-
393- void cancel()
394- {
395- timer.cancel();
396- }
397-
398- void on_timeout(const boost::system::error_code& ec)
399- {
400- if (ec)
401- return;
402-
403- if (ec != boost::asio::error::operation_aborted)
404- {
405- traits::Timeout<UnderlyingTimeoutType>::invoke_timeout_handler(timeout);
406- restart_or_cancel();
407- }
408- }
409-
410- boost::asio::io_service& io_service;
411- boost::asio::deadline_timer timer;
412- UnderlyingTimeoutType* timeout;
413- };
414-
415- template<typename UnderlyingWatchType = DBusWatch>
416- struct Watch : std::enable_shared_from_this<Watch<UnderlyingWatchType>>
417- {
418- Watch(boost::asio::io_service& io_service, UnderlyingWatchType* watch) : io_service(io_service),
419- stream_descriptor(io_service),
420- watch(watch)
421- {
422- if (!watch)
423- throw std::runtime_error("Precondition violated: watch has to be non-null");
424- }
425-
426- ~Watch() noexcept
427- {
428- stream_descriptor.cancel();
429- stream_descriptor.release();
430- }
431-
432- void start()
433- {
434- stream_descriptor.assign(traits::Watch<UnderlyingWatchType>::get_watch_unix_fd(watch));
435- restart();
436- }
437-
438- void restart()
439- {
440- if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_readable(watch))
441- {
442- stream_descriptor.async_read_some(
443- boost::asio::null_buffers(),
444- std::bind(
445- &Watch::on_stream_descriptor_event,
446- Watch<UnderlyingWatchType>::shared_from_this(),
447- traits::Watch<UnderlyingWatchType>::readable_event(),
448- std::placeholders::_1,
449- std::placeholders::_2));
450- }
451- if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_writable(watch))
452- {
453- stream_descriptor.async_write_some(
454- boost::asio::null_buffers(),
455- std::bind(
456- &Watch::on_stream_descriptor_event,
457- Watch<UnderlyingWatchType>::shared_from_this(),
458- traits::Watch<UnderlyingWatchType>::writeable_event(),
459- std::placeholders::_1,
460- std::placeholders::_2));
461- }
462- }
463-
464- void cancel()
465- {
466- try
467- {
468- stream_descriptor.cancel();
469- }
470- catch (...)
471- {
472- }
473- }
474-
475- void on_stream_descriptor_event(int event, const boost::system::error_code& error, std::size_t)
476- {
477- if (error == boost::asio::error::operation_aborted)
478- {
479- return;
480- }
481-
482- if (error)
483- {
484- traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event(
485- watch,
486- traits::Watch<UnderlyingWatchType>::error_event());
487- }
488- else
489- {
490- if (!traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event(watch, event))
491- throw std::runtime_error("Insufficient memory while handling watch event");
492-
493- restart();
494- }
495- }
496-
497- boost::asio::io_service& io_service;
498- boost::asio::posix::stream_descriptor stream_descriptor;
499- UnderlyingWatchType* watch;
500- };
501-
502- template<typename T>
503- struct Holder
504- {
505- static void ptr_delete(void* p)
506- {
507- delete static_cast<Holder<T>*>(p);
508- }
509-
510- Holder(const T& t) : value(t)
511- {
512- }
513-
514- T value;
515- };
516-
517- static dbus_bool_t on_dbus_add_watch(DBusWatch* watch, void* data)
518- {
519- if (dbus_watch_get_enabled(watch) == FALSE)
520- return TRUE;
521-
522- auto thiz = static_cast<Executor*>(data);
523- auto w = std::shared_ptr<Watch<>>(new Watch<>(thiz->io_service, watch));
524- auto holder = new Holder<std::shared_ptr<Watch<>>>(w);
525- dbus_watch_set_data(watch, holder, Holder<std::shared_ptr<Watch<>>>::ptr_delete);
526-
527- w->start();
528-
529- return TRUE;
530- }
531-
532- static void on_dbus_remove_watch(DBusWatch* watch, void*)
533- {
534- auto w = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch));
535- if (!w)
536- return;
537- w->value->cancel();
538- }
539-
540- static void on_dbus_watch_toggled(DBusWatch* watch, void*)
541- {
542- auto holder = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch));
543- if (!holder)
544- return;
545- dbus_watch_get_enabled(watch) == TRUE ? holder->value->restart() : holder->value->cancel();
546- }
547-
548- static dbus_bool_t on_dbus_add_timeout(DBusTimeout* timeout, void* data)
549- {
550- auto thiz = static_cast<Executor*>(data);
551- auto t = std::shared_ptr<Timeout<>>(new Timeout<>(thiz->io_service, timeout));
552- auto holder = new Holder<std::shared_ptr<Timeout<>>>(t);
553- dbus_timeout_set_data(
554- timeout,
555- holder,
556- Holder<std::shared_ptr<Timeout<>>>::ptr_delete);
557-
558- t->start();
559- return true;
560- }
561-
562- static void on_dbus_remove_timeout(DBusTimeout* timeout, void*)
563- {
564- static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout))->value->cancel();
565- }
566-
567- static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*)
568- {
569- auto holder = static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout));
570- holder->value->restart_or_cancel();
571- }
572-
573- static void on_dbus_wakeup_event_loop(void* data)
574- {
575- auto thiz = static_cast<Executor*>(data);
576- thiz->io_service.post(std::bind(dbus_connection_dispatch, thiz->bus->raw()));
577- }
578-
579- public:
580- explicit Executor(const Bus::Ptr& bus) : bus(bus), work(io_service)
581- {
582- if (!bus)
583- throw std::runtime_error("Precondition violated, cannot construct executor for null bus.");
584-
585- if (!dbus_connection_set_watch_functions(
586- bus->raw(),
587- on_dbus_add_watch,
588- on_dbus_remove_watch,
589- on_dbus_watch_toggled,
590- this,
591- nullptr))
592- throw std::runtime_error("Problem installing watch functions.");
593-
594- if (!dbus_connection_set_timeout_functions(
595- bus->raw(),
596- on_dbus_add_timeout,
597- on_dbus_remove_timeout,
598- on_dbus_timeout_toggled,
599- this,
600- nullptr))
601- throw std::runtime_error("Problem installing timeout functions.");
602-
603- dbus_connection_set_wakeup_main_function(
604- bus->raw(),
605- on_dbus_wakeup_event_loop,
606- this,
607- nullptr);
608- }
609-
610- ~Executor() noexcept
611- {
612- stop();
613- }
614-
615- void run()
616- {
617- io_service.run();
618- }
619-
620- void stop()
621- {
622- io_service.stop();
623- }
624-
625- private:
626- Bus::Ptr bus;
627- boost::asio::io_service io_service;
628- boost::asio::io_service::work work;
629-};
630-}
631-}
632-}
633-}
634
635=== modified file 'src/core/dbus/bus.cpp'
636--- src/core/dbus/bus.cpp 2014-01-25 16:08:10 +0000
637+++ src/core/dbus/bus.cpp 2014-03-05 07:16:59 +0000
638@@ -252,6 +252,16 @@
639 const std::shared_ptr<Message>& msg,
640 const std::chrono::milliseconds& milliseconds)
641 {
642+ // TODO(tvoss): Enable this once method handlers have been adjusted to
643+ // operate on contexts.
644+ // if (d->executor)
645+ // {
646+ // throw std::logic_error("Calling Bus::send_with_reply_and_block_for_at_most on a "
647+ // "connection that is run by an executor, i.e., an event loop "
648+ // "is not supported as the underlying implementation in libdbus "
649+ // "is racy");
650+ //}
651+
652 Error se;
653
654 auto result = dbus_connection_send_with_reply_and_block(
655@@ -277,7 +287,7 @@
656 std::addressof(pending_call),
657 timeout.count());
658
659- if (!result)
660+ if (result == FALSE)
661 throw Errors::NoMemory{};
662
663 if (!pending_call)
664
665=== modified file 'src/core/dbus/dbus.cpp'
666--- src/core/dbus/dbus.cpp 2014-01-27 15:09:15 +0000
667+++ src/core/dbus/dbus.cpp 2014-03-05 07:16:59 +0000
668@@ -146,6 +146,7 @@
669
670 std::string DBus::hello() const
671 {
672+ // TODO: We really should switch to method transaction here.
673 return object->invoke_method_synchronously<Hello, std::string>().value();
674 }
675
676
677=== modified file 'src/core/dbus/pending_call_impl.h'
678--- src/core/dbus/pending_call_impl.h 2013-11-27 18:57:42 +0000
679+++ src/core/dbus/pending_call_impl.h 2014-03-05 07:16:59 +0000
680@@ -24,6 +24,7 @@
681
682 #include <dbus/dbus.h>
683
684+#include <condition_variable>
685 #include <mutex>
686
687 namespace core
688@@ -37,54 +38,73 @@
689 private:
690 struct Wrapper
691 {
692- std::shared_ptr<PendingCall> pending_call;
693+ std::shared_ptr<core::dbus::impl::PendingCall> pending_call;
694 };
695
696 static void on_pending_call_completed(DBusPendingCall* call,
697 void* cookie)
698 {
699 auto wrapper = static_cast<Wrapper*>(cookie);
700- std::lock_guard<std::mutex> lg(wrapper->pending_call->callback_guard);
701- if (wrapper->pending_call->callback)
702+
703+ auto message = dbus_pending_call_steal_reply(call);
704+
705+ if (message)
706 {
707- auto message = dbus_pending_call_steal_reply(call);
708- if (message)
709- wrapper->pending_call->callback(Message::from_raw_message(message));
710+ wrapper->pending_call->notify(Message::from_raw_message(message));
711 }
712 }
713
714+ void notify(const Message::Ptr& msg)
715+ {
716+ std::lock_guard<std::mutex> lg(guard);
717+
718+ message = msg;
719+
720+ if (callback)
721+ callback(message);
722+
723+ }
724+
725 DBusPendingCall* pending_call;
726- std::mutex callback_guard;
727- core::dbus::PendingCall::Notification callback;
728+ std::mutex guard;
729+ Message::Ptr message;
730+ PendingCall::Notification callback;
731
732 public:
733 inline static core::dbus::PendingCall::Ptr create(DBusPendingCall* call)
734 {
735- auto result = std::shared_ptr<core::dbus::impl::PendingCall>(
736- new core::dbus::impl::PendingCall(call));
737-
738- dbus_pending_call_set_notify(
739- result->pending_call,
740- PendingCall::on_pending_call_completed,
741- new Wrapper{result},
742- [](void* data) { delete static_cast<Wrapper*>(data); });
743+ auto result = std::shared_ptr<core::dbus::impl::PendingCall>
744+ {
745+ new core::dbus::impl::PendingCall{call}
746+ };
747+
748+ if (FALSE == dbus_pending_call_set_notify(
749+ result->pending_call,
750+ PendingCall::on_pending_call_completed,
751+ new Wrapper{result},
752+ [](void* data)
753+ {
754+ delete static_cast<Wrapper*>(data);
755+ }))
756+ {
757+ throw std::runtime_error("Error setting up pending call notification.");
758+ }
759+
760+ // And here comes the beauty of libdbus, and its racy architecture:
761+ {
762+ std::lock_guard<std::mutex> lg(result->guard);
763+ if (TRUE == dbus_pending_call_get_completed(call))
764+ {
765+ // We took too long while setting up the pending call notification.
766+ // For that we now have to inject the message here.
767+ auto msg = dbus_pending_call_steal_reply(call);
768+ result->message = Message::from_raw_message(msg);
769+ }
770+ }
771
772 return std::dynamic_pointer_cast<core::dbus::PendingCall>(result);
773 }
774
775- inline std::shared_ptr<Message> wait_for_reply()
776- {
777- dbus_pending_call_block(pending_call);
778- auto reply = dbus_pending_call_steal_reply(pending_call);
779-
780- if (!reply)
781- {
782- // TODO(tvoss): Throw PendingCallYieldedEmptyReply.
783- }
784-
785- return Message::from_raw_message(reply);
786- }
787-
788 void cancel()
789 {
790 dbus_pending_call_cancel(pending_call);
791@@ -92,14 +112,20 @@
792
793 void then(const core::dbus::PendingCall::Notification& notification)
794 {
795- std::lock_guard<std::mutex> lg{callback_guard};
796+ std::lock_guard<std::mutex> lg(guard);
797 callback = notification;
798+
799+ // We already have a reply and invoke the callback directly.
800+ if (message)
801+ callback(message);
802 }
803
804 private:
805 PendingCall(DBusPendingCall* call)
806 : pending_call(call)
807 {
808+
809+
810 }
811 };
812 }
813
814=== modified file 'tests/bus_test.cpp'
815--- tests/bus_test.cpp 2014-01-10 07:34:59 +0000
816+++ tests/bus_test.cpp 2014-03-05 07:16:59 +0000
817@@ -88,13 +88,28 @@
818 "ListNames");
819
820 auto bus = session_bus();
821+ bus->install_executor(dbus::asio::make_executor(bus));
822+ std::thread t{[bus](){bus->run();}};
823+
824+ std::this_thread::sleep_for(std::chrono::seconds{1});
825+
826 const std::chrono::milliseconds timeout = std::chrono::seconds(10);
827
828 auto call = bus->send_with_reply_and_timeout(msg, timeout);
829- auto reply = call->wait_for_reply();
830- EXPECT_EQ(dbus::Message::Type::method_return, reply->type());
831- std::vector<std::string> result; reply->reader() >> result;
832- EXPECT_TRUE(result.size() > 0);
833+ auto promise = std::make_shared<std::promise<std::vector<std::string>>>();
834+ auto future = promise->get_future();
835+ call->then([promise](const core::dbus::Message::Ptr& reply)
836+ {
837+ std::vector<std::string> result; reply->reader() >> result;
838+ promise->set_value(result);
839+ });
840+
841+ EXPECT_TRUE(future.get().size() > 0);
842+
843+ bus->stop();
844+
845+ if (t.joinable())
846+ t.join();
847 }
848
849 TEST_F(Bus, HasOwnerForNameReturnsTrueForExistingName)
850
851=== modified file 'tests/dbus_test.cpp'
852--- tests/dbus_test.cpp 2014-02-18 06:43:55 +0000
853+++ tests/dbus_test.cpp 2014-03-05 07:16:59 +0000
854@@ -98,12 +98,16 @@
855 object->install_method_handler<test::Service::Method>(handler);
856 barrier.try_signal_ready_for(std::chrono::milliseconds{500});
857
858- std::thread t{[bus](){ bus->run(); }};
859+ std::thread t1{[bus](){ bus->run(); }};
860+ std::thread t2{[bus](){ bus->run(); }};
861
862 sc.wait_for_signal();
863
864- if (t.joinable())
865- t.join();
866+ if (t1.joinable())
867+ t1.join();
868+
869+ if (t2.joinable())
870+ t2.join();
871
872 EXPECT_EQ(pid, sender_pid);
873 EXPECT_EQ(uid, sender_uid);
874
875=== modified file 'tests/executor_test.cpp'
876--- tests/executor_test.cpp 2014-02-18 06:43:55 +0000
877+++ tests/executor_test.cpp 2014-03-05 07:16:59 +0000
878@@ -36,6 +36,22 @@
879
880 namespace
881 {
882+// A very simple wrapper for flipping a coin whether something should go wrong.
883+// Please note that we are considering an unfair coin here, with a probability of 0.2
884+// for failure.
885+struct ChaosMonkey
886+{
887+ constexpr static const double probability_for_failure{0.2};
888+
889+ bool thinks_that_something_should_go_wrong()
890+ {
891+ return coin(rng) == 1;
892+ }
893+
894+ std::mt19937 rng{42};
895+ std::binomial_distribution<> coin{1, 1.-probability_for_failure};
896+};
897+
898 struct Executor : public core::dbus::testing::Fixture
899 {
900 };
901@@ -127,6 +143,125 @@
902 EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
903 }
904
905+TEST_F(Executor, TimeoutsAreHandledCorrectly)
906+{
907+ core::testing::CrossProcessSync cross_process_sync;
908+
909+ const int64_t expected_value = 42;
910+ auto service = [this, expected_value, &cross_process_sync]()
911+ {
912+ ChaosMonkey chaos_monkey;
913+ core::testing::SigTermCatcher sc;
914+ auto bus = session_bus();
915+ bus->install_executor(dbus::asio::make_executor(bus));
916+ auto service = dbus::Service::add_service<test::Service>(bus);
917+ auto skeleton = service->add_object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));
918+ skeleton->install_method_handler<test::Service::Method>(
919+ [bus, skeleton, expected_value, &chaos_monkey](const dbus::Message::Ptr& msg)
920+ {
921+ // Let's add some randomness to this test and see if things are still stable.
922+ // In this case, chaos means an early return and no reply being sent.
923+ if (chaos_monkey.thinks_that_something_should_go_wrong())
924+ return;
925+
926+ auto reply = dbus::Message::make_method_return(msg);
927+ reply->writer() << expected_value;
928+ bus->send(reply);
929+ });
930+
931+ cross_process_sync.try_signal_ready_for(std::chrono::milliseconds{500});
932+
933+ std::thread w1([bus]() { bus->run(); });
934+ std::thread w2([bus]() { bus->run(); });
935+
936+ sc.wait_for_signal();
937+
938+ bus->stop();
939+
940+ if (w1.joinable())
941+ w1.join();
942+
943+ if (w2.joinable())
944+ w2.join();
945+
946+ return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
947+ };
948+
949+ auto client = [this, expected_value, &cross_process_sync]() -> core::posix::exit::Status
950+ {
951+ auto bus = session_bus();
952+ bus->install_executor(dbus::asio::make_executor(bus));
953+ std::thread t{[bus](){bus->run();}};
954+
955+ // If you encounter failures in the client, uncomment the following two lines
956+ // and follow the instructions on the terminal.
957+ // std::cout << "Invoke gdb with: sudo gdb -p " << getpid() << std::endl;
958+ // sleep(10);
959+
960+ EXPECT_EQ(std::uint32_t(1), cross_process_sync.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
961+
962+ auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<test::Service>::interface_name());
963+ auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));
964+
965+ std::thread updater1([bus, stub]()
966+ {
967+ for (unsigned int counter = 0; counter < 1000; counter++)
968+ {
969+ try
970+ {
971+ auto result = stub->transact_method<test::Service::Method, int64_t>();
972+
973+ if (result.is_error())
974+ std::cout << result.error().print() << std::endl;
975+ else
976+ EXPECT_EQ(42, result.value());
977+
978+ } catch(const std::runtime_error& e)
979+ {
980+ std::cout << e.what() << std::endl;
981+ }
982+ }
983+ });
984+
985+ std::thread updater2([bus, stub]()
986+ {
987+ for (unsigned int counter = 0; counter < 1000; counter++)
988+ {
989+ try
990+ {
991+ auto result = stub->transact_method<test::Service::Method, int64_t>();
992+
993+ if (result.is_error())
994+ std::cout << result.error().print() << std::endl;
995+ else
996+ EXPECT_EQ(42, result.value());
997+
998+ } catch(const std::runtime_error& e)
999+ {
1000+ std::cout << e.what() << std::endl;
1001+ }
1002+ }
1003+ });
1004+
1005+ if (updater1.joinable())
1006+ updater1.join();
1007+
1008+ if (updater2.joinable())
1009+ updater2.join();
1010+
1011+ bus->stop();
1012+
1013+ if (t.joinable())
1014+ t.join();
1015+
1016+ return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
1017+ };
1018+
1019+ EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
1020+}
1021+
1022+
1023+
1024 /*TEST(Bus, TimeoutThrowsForNullDBusWatch)
1025 {
1026 boost::asio::io_service io_service;
1027
1028=== modified file 'tests/signal_delivery_test.cpp'
1029--- tests/signal_delivery_test.cpp 2014-02-18 06:43:55 +0000
1030+++ tests/signal_delivery_test.cpp 2014-03-05 07:16:59 +0000
1031@@ -323,8 +323,8 @@
1032 if (t.joinable())
1033 t.join();
1034
1035- EXPECT_EQ(std::uint32_t(1), received1);
1036- EXPECT_EQ(std::uint32_t(1), received2);
1037+ EXPECT_EQ(std::uint32_t(2), received1);
1038+ EXPECT_EQ(std::uint32_t(2), received2);
1039
1040 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
1041 };
1042
1043=== modified file 'tests/test_service.h'
1044--- tests/test_service.h 2014-02-18 06:43:55 +0000
1045+++ tests/test_service.h 2014-03-05 07:16:59 +0000
1046@@ -38,7 +38,7 @@
1047
1048 inline static const std::chrono::milliseconds default_timeout()
1049 {
1050- return std::chrono::seconds{1};
1051+ return std::chrono::milliseconds{10};
1052 }
1053 };
1054
1055@@ -126,7 +126,6 @@
1056 {
1057 "this.is.unlikely.to.exist.Service"
1058 };
1059- std::cout << __PRETTY_FUNCTION__ << ": " << s << std::endl;
1060 return s;
1061 }
1062 };

Subscribers

People subscribed via source and target branches