Merge lp:~thomas-voss/dbus-cpp/decouple-executor-and-bus into lp:dbus-cpp/15.04

Proposed by Thomas Voß
Status: Needs review
Proposed branch: lp:~thomas-voss/dbus-cpp/decouple-executor-and-bus
Merge into: lp:dbus-cpp/15.04
Diff against target: 1554 lines (+848/-466)
11 files modified
CMakeLists.txt (+2/-0)
include/core/dbus/asio/make_executor.h (+5/-5)
include/core/dbus/executor.h (+86/-10)
src/core/dbus/CMakeLists.txt (+5/-1)
src/core/dbus/asio/asio.cpp (+172/-383)
src/core/dbus/asio/asio.h (+108/-0)
src/core/dbus/bus.cpp (+251/-2)
src/core/dbus/executor.cpp (+42/-0)
tests/CMakeLists.txt (+1/-0)
tests/executor_test.cpp (+145/-51)
tests/service_test.cpp (+31/-14)
To merge this branch: bzr merge lp:~thomas-voss/dbus-cpp/decouple-executor-and-bus
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+282977@code.launchpad.net

Commit message

Decouple dbus::Executor/dbus::Bus and prevent from ownership loops.

Description of the change

Decouple dbus::Executor/dbus::Bus and prevent from ownership loops.

To post a comment you must log in.

Unmerged revisions

107. By Thomas Voß

Fixes lp:1501864.

106. By Thomas Voß

Decouple dbus::Executor/dbus::Bus and prevent from ownership loops.

105. By Thomas Voß

Decouple dbus::Executor and dbus::Bus.

104. By Thomas Voß

Add interface headers such that IDEs can pick them up.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-11-10 08:35:29 +0000
+++ CMakeLists.txt 2016-01-18 16:49:39 +0000
@@ -67,6 +67,8 @@
6767
68include(CTest)68include(CTest)
6969
70file(GLOB_RECURSE DBUS_CPP_INTERFACE_HEADERS include/*.h)
71
70include_directories(72include_directories(
71 include73 include
72 ${LIBXML2_INCLUDE_DIR}74 ${LIBXML2_INCLUDE_DIR}
7375
=== renamed file 'include/core/dbus/asio/executor.h' => 'include/core/dbus/asio/make_executor.h'
--- include/core/dbus/asio/executor.h 2014-06-09 08:56:57 +0000
+++ include/core/dbus/asio/make_executor.h 2016-01-18 16:49:39 +0000
@@ -15,8 +15,8 @@
15 *15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */17 */
18#ifndef CORE_DBUS_ASIO_EXECUTOR_H_18#ifndef CORE_DBUS_ASIO_MAKE_EXECUTOR_H_
19#define CORE_DBUS_ASIO_EXECUTOR_H_19#define CORE_DBUS_ASIO_MAKE_EXECUTOR_H_
2020
21#include <core/dbus/bus.h>21#include <core/dbus/bus.h>
22#include <core/dbus/executor.h>22#include <core/dbus/executor.h>
@@ -36,10 +36,10 @@
36{36{
37namespace asio37namespace asio
38{38{
39ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus);39ORG_FREEDESKTOP_DBUS_DLL_PUBLIC core::dbus::Executor::Ptr make_executor(const Bus::Ptr& bus);
40ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus, boost::asio::io_service& io);40ORG_FREEDESKTOP_DBUS_DLL_PUBLIC core::dbus::Executor::Ptr make_executor(const Bus::Ptr& bus, const std::shared_ptr<boost::asio::io_service>& io);
41}41}
42}42}
43}43}
4444
45#endif // CORE_DBUS_ASIO_EXECUTOR_H_45#endif // CORE_DBUS_ASIO_MAKE_EXECUTOR_H_
4646
=== modified file 'include/core/dbus/executor.h'
--- include/core/dbus/executor.h 2013-11-27 18:57:42 +0000
+++ include/core/dbus/executor.h 2016-01-18 16:49:39 +0000
@@ -20,6 +20,10 @@
2020
21#include <core/dbus/visibility.h>21#include <core/dbus/visibility.h>
2222
23#include <core/signal.h>
24
25#include <functional>
26#include <iosfwd>
23#include <memory>27#include <memory>
2428
25namespace core29namespace core
@@ -34,25 +38,97 @@
34public:38public:
35 typedef std::shared_ptr<Executor> Ptr;39 typedef std::shared_ptr<Executor> Ptr;
36 40
41 /** @brief Task models an arbitrary function to be executed by an Executor. */
42 typedef std::function<void()> Task;
43
44 /** @brief A Timeout models an individual recurring timer. */
45 class Timeout
46 {
47 public:
48 /** @cond */
49 typedef std::shared_ptr<Timeout> Ptr;
50
51 Timeout(Timeout&&) = delete;
52 Timeout(const Timeout&) = delete;
53 Timeout&& operator=(Timeout&&) = delete;
54 Timeout& operator=(const Timeout&) = delete;
55 virtual ~Timeout() = default;
56 /** @endcond */
57
58 /** @brief start enables the timer. */
59 virtual void start() = 0;
60 /** @brief stop stops the timer. */
61 virtual void stop() = 0;
62 /** @brief triggered returns a signal that is emitted whenever the timer expires. */
63 virtual const core::Signal<void>& on_triggered() = 0;
64
65 protected:
66 Timeout() = default;
67 };
68
69 /** @brief A Timeout models a recurring timer. */
70 class Watch
71 {
72 public:
73 /** @cond */
74 typedef std::shared_ptr<Watch> Ptr;
75 /** @endcond */
76
77 /** @brief Flags enumerates all possible operations and result codes when watching file descriptors. */
78 enum class Flags : std::uint8_t
79 {
80 none = 0,
81 readable = 1 << 0,
82 writable = 1 << 1,
83 error = 1 << 2,
84 hangup = 1 << 3
85 };
86
87 /** @cond */
88 Watch(Watch&&) = delete;
89 Watch(const Watch&) = delete;
90 Watch&& operator=(Watch&&) = delete;
91 Watch& operator=(const Watch&) = delete;
92 virtual ~Watch() = default;
93 /** @endcond */
94
95 /** @brief start enables the watch. */
96 virtual void start_with_flags(Watch::Flags flags) = 0;
97 /** @brief stop stops the watch. */
98 virtual void stop() = 0;
99 /** @brief triggered returns a signal that is emitted when the underlying fd becomes read/writable. */
100 virtual const core::Signal<Flags>& on_triggered() = 0;
101
102 protected:
103 Watch() = default;
104 };
105
37 virtual ~Executor() = default;106 virtual ~Executor() = default;
38107
108 /** @brief add_watch returns a Watch instance monitoring fd. */
109 virtual Watch::Ptr add_watch(int fd) = 0;
110 /** @brief add_timeout returns a Timeout instance. */
111 virtual Timeout::Ptr add_timeout(const std::chrono::milliseconds& duration) = 0;
112 /** @brief dispatch takes task and executes it either now or later. */
113 virtual void dispatch(const Task& task) = 0;
114
115 /** @brief Start the event loop and block until stop is called. */
116 virtual void run() = 0;
117
118 /** @brief Stop the event loop. */
119 virtual void stop() = 0;
120
39protected:121protected:
40 friend class Bus;122 friend class Bus;
41123
42 Executor() = default;124 Executor() = default;
43 Executor(const Executor&) = delete;125 Executor(const Executor&) = delete;
44 Executor& operator=(const Executor&) = delete;126 Executor& operator=(const Executor&) = delete;
45
46 /**
47 * @brief Start the event loop and block until stop is called.
48 */
49 virtual void run() = 0;
50
51 /**
52 * @brief Stop the event loop.
53 */
54 virtual void stop() = 0;
55};127};
128
129ORG_FREEDESKTOP_DBUS_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, Executor::Watch::Flags flags);
130ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Watch::Flags operator|(Executor::Watch::Flags lhs, Executor::Watch::Flags rhs);
131ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Watch::Flags operator&(Executor::Watch::Flags lhs, Executor::Watch::Flags rhs);
56}132}
57}133}
58134
59135
=== modified file 'src/core/dbus/CMakeLists.txt'
--- src/core/dbus/CMakeLists.txt 2014-03-05 15:02:12 +0000
+++ src/core/dbus/CMakeLists.txt 2016-01-18 16:49:39 +0000
@@ -21,6 +21,7 @@
2121
22include_directories(22include_directories(
23 ${CMAKE_SOURCE_DIR}/include23 ${CMAKE_SOURCE_DIR}/include
24 ${CMAKE_SOURCE_DIR}/src
24 ${DBUS_INCLUDE_DIRS}25 ${DBUS_INCLUDE_DIRS}
25 ${Boost_INCLUDE_DIRS}26 ${Boost_INCLUDE_DIRS}
26 ${PROCESS_CPP_INCLUDE_DIR}27 ${PROCESS_CPP_INCLUDE_DIR}
@@ -33,17 +34,20 @@
33add_library(34add_library(
34 dbus-cpp SHARED35 dbus-cpp SHARED
3536
37 ${DBUS_CPP_INTERFACE_HEADERS}
36 ${CMAKE_CURRENT_BINARY_DIR}/fixture.cpp38 ${CMAKE_CURRENT_BINARY_DIR}/fixture.cpp
3739
38 bus.cpp40 bus.cpp
39 dbus.cpp41 dbus.cpp
42 executor.cpp
40 error.cpp43 error.cpp
41 match_rule.cpp44 match_rule.cpp
42 message.cpp45 message.cpp
43 service.cpp46 service.cpp
44 service_watcher.cpp47 service_watcher.cpp
4548
46 asio/executor.cpp49 asio/asio.h
50 asio/asio.cpp
4751
48 types/object_path.cpp52 types/object_path.cpp
49)53)
5054
=== renamed file 'src/core/dbus/asio/executor.cpp' => 'src/core/dbus/asio/asio.cpp'
--- src/core/dbus/asio/executor.cpp 2014-09-28 21:27:09 +0000
+++ src/core/dbus/asio/asio.cpp 2016-01-18 16:49:39 +0000
@@ -16,10 +16,9 @@
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */17 */
1818
19#include <core/dbus/bus.h>
20#include <core/dbus/executor.h>19#include <core/dbus/executor.h>
21#include <core/dbus/traits/timeout.h>20#include <core/dbus/asio/asio.h>
22#include <core/dbus/traits/watch.h>21#include <core/dbus/asio/make_executor.h>
2322
24#include <boost/asio.hpp>23#include <boost/asio.hpp>
25#include <boost/asio/io_service.hpp>24#include <boost/asio/io_service.hpp>
@@ -32,383 +31,173 @@
32#include <future>31#include <future>
33#include <thread>32#include <thread>
3433
35namespace core34core::dbus::asio::Executor::Timeout::Ptr core::dbus::asio::Executor::Timeout::create(const std::shared_ptr<boost::asio::io_service>& io_service, const std::chrono::milliseconds& period)
36{35{
37namespace dbus36 return Executor::Timeout::Ptr(new Executor::Timeout(io_service, period));
38{37}
39namespace traits38
40{39core::dbus::asio::Executor::Timeout::Timeout(const std::shared_ptr<boost::asio::io_service>& ios, const std::chrono::milliseconds& ms)
41template<>40 : ios(ios),
42struct Timeout<DBusTimeout>41 timer(*ios),
43{42 period(ms)
44 typedef int DurationType;43{
4544}
46 static inline bool is_timeout_enabled(DBusTimeout* timeout)45
47 {46core::dbus::asio::Executor::Timeout::~Timeout()
48 return TRUE == dbus_timeout_get_enabled(timeout);47{
49 }48 // cancel();
5049}
51 static inline int get_timeout_interval(DBusTimeout* timeout)50
52 {51void core::dbus::asio::Executor::Timeout::start()
53 return DurationType(dbus_timeout_get_interval(timeout));52{
54 }53 // We do not keep ourselves alive to prevent from races during destruction.
5554 std::weak_ptr<Timeout> wp{this->shared_from_this()};
56 static inline void invoke_timeout_handler(DBusTimeout* timeout)55
57 {56 timer.expires_from_now(boost::posix_time::milliseconds(period.count()));
58 dbus_timeout_handle(timeout);57 timer.async_wait([wp](const boost::system::error_code& ec)
59 }58 {
60};59 if (auto sp = wp.lock())
6160 sp->on_timeout(ec);
62template<>61 });
63struct Watch<DBusWatch>62}
64{63
65 inline static int readable_event() { return DBUS_WATCH_READABLE; }64void core::dbus::asio::Executor::Timeout::stop()
66 inline static int writeable_event() { return DBUS_WATCH_WRITABLE; }65{
67 inline static int error_event() { return DBUS_WATCH_ERROR; }66 try
68 inline static int hangup_event() { return DBUS_WATCH_HANGUP; }67 {
6968 timer.cancel();
70 static inline bool is_watch_enabled(DBusWatch* watch)69 } catch(...)
71 {70 {
72 return TRUE == dbus_watch_get_enabled(watch);71 // Really not sure what we should do about exceptions here.
73 }72 }
7473}
75 static inline int get_watch_unix_fd(DBusWatch* watch)74
76 {75const core::Signal<void>& core::dbus::asio::Executor::Timeout::on_triggered()
77 return dbus_watch_get_unix_fd(watch);76{
78 }77 return triggered;
7978}
80 static inline bool is_watch_monitoring_fd_for_readable(DBusWatch* watch)79
81 {80void core::dbus::asio::Executor::Timeout::on_timeout(const boost::system::error_code& ec)
82 return dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE;81{
83 }82 if (not ec)
8483 triggered();
85 static bool is_watch_monitoring_fd_for_writable(DBusWatch* watch)84}
86 {85
87 return dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE;86core::dbus::asio::Executor::Watch::Ptr core::dbus::asio::Executor::Watch::create(const std::shared_ptr<boost::asio::io_service>& io_service, int fd)
88 }87{
8988 return Executor::Watch::Ptr(new Executor::Watch(io_service, fd));
90 static bool invoke_watch_handler_for_event(DBusWatch* watch, int event)89}
91 {90
92 return dbus_watch_handle(watch, event);91core::dbus::asio::Executor::Watch::Watch(const std::shared_ptr<boost::asio::io_service>& ios, int fd)
93 }92 : ios(ios),
94};93 stream_descriptor(*ios, ::dup(fd))
95}94{
96namespace asio95}
97{96
98class Executor : public core::dbus::Executor97core::dbus::asio::Executor::Watch::~Watch()
99{98{
100public:99}
101 template<typename UnderlyingTimeoutType = DBusTimeout>100
102 struct Timeout : std::enable_shared_from_this<Timeout<UnderlyingTimeoutType>>101void core::dbus::asio::Executor::Watch::start_with_flags(Watch::Flags flags)
103 {102{
104 Timeout(boost::asio::io_service& io_service, UnderlyingTimeoutType* timeout)103 // We do not keep ourselves alive to prevent from races during destruction.
105 : io_service(io_service),104 std::weak_ptr<Watch> wp{this->shared_from_this()};
106 timer(io_service),105
107 timeout(timeout)106 if ((flags & Flags::readable) == Flags::readable)
108 {107 {
109 if (!timeout)108 stream_descriptor.async_read_some(boost::asio::null_buffers(), [wp, flags](boost::system::error_code ec, std::size_t bytes_transferred)
110 throw std::runtime_error("Precondition violated: timeout has to be non-null");109 {
111 }110 if (auto sp = wp.lock())
112111 {
113 ~Timeout()112 if (ec != boost::asio::error::operation_aborted)
114 {113 sp->on_stream_descriptor_event(ec ? Flags::error : Flags::readable, flags, bytes_transferred);
115 // cancel();114 }
116 }115 });
117116 }
118 void start()117
119 {118 if ((flags & Flags::writable) == Flags::writable)
120 if (!traits::Timeout<UnderlyingTimeoutType>::is_timeout_enabled(timeout))119 {
121 {120 stream_descriptor.async_write_some(boost::asio::null_buffers(), [wp, flags](boost::system::error_code ec, std::size_t bytes_transferred)
122 return;121 {
123 }122 if (auto sp = wp.lock())
124123 {
125 // We do not keep ourselves alive to prevent from races during destruction.124 if (ec != boost::asio::error::operation_aborted)
126 std::weak_ptr<Timeout<UnderlyingTimeoutType>> wp{this->shared_from_this()};125 sp->on_stream_descriptor_event(ec ? Flags::error : Flags::writable, flags, bytes_transferred);
127126 }
128 timer.expires_from_now(127 });
129 boost::posix_time::milliseconds(128 }
130 traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval(129}
131 timeout)));130
132 timer.async_wait([wp](const boost::system::error_code& ec)131void core::dbus::asio::Executor::Watch::stop()
133 {132{
134 auto sp = wp.lock();133 try
135134 {
136 if (sp)135 stream_descriptor.cancel();
137 sp->on_timeout(ec);136 }
138 });137 catch (...)
139 }138 {
140139 }
141 void cancel()140}
142 {141
143 try142const core::Signal<core::dbus::Executor::Watch::Flags>& core::dbus::asio::Executor::Watch::on_triggered()
144 {143{
145 timer.cancel();144 return triggered;
146 } catch(...)145}
147 {146
148 // Really not sure what we should do about exceptions here.147void core::dbus::asio::Executor::Watch::on_stream_descriptor_event(Watch::Flags flags, Watch::Flags requested, std::size_t)
149 }148{
150 }149 const bool has_error = (flags & Flags::error) == Flags::error;
151150
152 void on_timeout(const boost::system::error_code& ec)151 triggered(flags);
153 {152
154 if (ec == boost::asio::error::operation_aborted)153 if (not has_error)
155 return;154 start_with_flags(requested);
156155}
157 if (ec)156
158 return;157core::dbus::asio::Executor::Executor(const std::shared_ptr<boost::asio::io_service>& ios) : ios(ios), work(*ios)
159158{
160 traits::Timeout<UnderlyingTimeoutType>::invoke_timeout_handler(timeout);159}
161 }160
162161core::dbus::asio::Executor::~Executor()
163 boost::asio::io_service& io_service;162{
164 boost::asio::deadline_timer timer;163 stop();
165 UnderlyingTimeoutType* timeout;164}
166 };165
167166core::dbus::Executor::Watch::Ptr core::dbus::asio::Executor::add_watch(int fd)
168 template<typename UnderlyingWatchType = DBusWatch>167{
169 struct Watch : std::enable_shared_from_this<Watch<UnderlyingWatchType>>168 return asio::Executor::Watch::create(ios, fd);
170 {169}
171 Watch(boost::asio::io_service& io_service, UnderlyingWatchType* watch) : io_service(io_service),170
172 stream_descriptor(io_service),171core::dbus::Executor::Timeout::Ptr core::dbus::asio::Executor::add_timeout(const std::chrono::milliseconds& duration)
173 watch(watch)172{
174 {173 return asio::Executor::Timeout::create(ios, duration);
175 if (!watch)174}
176 throw std::runtime_error("Precondition violated: watch has to be non-null");175
177 }176void core::dbus::asio::Executor::dispatch(const Executor::Task& task)
178177{
179 ~Watch() noexcept178 ios->post([task]()
180 {179 {
181 stream_descriptor.cancel();180 task();
182 stream_descriptor.release();181 });
183 }182}
184183
185 void start()184void core::dbus::asio::Executor::run()
186 {185{
187 stream_descriptor.assign(traits::Watch<UnderlyingWatchType>::get_watch_unix_fd(watch));186 ios->run();
188 restart();187}
189 }188
190189void core::dbus::asio::Executor::stop()
191 void restart()190{
192 {191 ios->stop();
193 // We do not keep ourselves alive to prevent from races during destruction.192}
194 std::weak_ptr<Watch<UnderlyingWatchType>> wp{this->shared_from_this()};193
195194core::dbus::Executor::Ptr core::dbus::asio::make_executor(const Bus::Ptr&)
196 if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_readable(watch))195{
197 {196 static auto ios = std::make_shared<boost::asio::io_service>();
198 stream_descriptor.async_read_some(boost::asio::null_buffers(), [wp](boost::system::error_code ec, std::size_t bytes_transferred)197 return std::make_shared<core::dbus::asio::Executor>(ios);
199 {198}
200 auto sp = wp.lock();199
201200core::dbus::Executor::Ptr core::dbus::asio::make_executor(const Bus::Ptr&, const std::shared_ptr<boost::asio::io_service>& ios)
202 if (sp)201{
203 sp->on_stream_descriptor_event(202 return std::make_shared<core::dbus::asio::Executor>(ios);
204 traits::Watch<UnderlyingWatchType>::readable_event(),203}
205 ec,
206 bytes_transferred);
207 });
208 }
209
210 if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_writable(watch))
211 {
212 stream_descriptor.async_write_some(boost::asio::null_buffers(), [wp](boost::system::error_code ec, std::size_t bytes_transferred)
213 {
214 auto sp = wp.lock();
215
216 if (sp)
217 sp->on_stream_descriptor_event(
218 traits::Watch<UnderlyingWatchType>::writeable_event(),
219 ec,
220 bytes_transferred);
221 });
222 }
223 }
224
225 void cancel()
226 {
227 try
228 {
229 stream_descriptor.cancel();
230 }
231 catch (...)
232 {
233 }
234 }
235
236 void on_stream_descriptor_event(int event, const boost::system::error_code& error, std::size_t)
237 {
238 if (error == boost::asio::error::operation_aborted)
239 {
240 return;
241 }
242
243 if (error)
244 {
245 traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event(
246 watch,
247 traits::Watch<UnderlyingWatchType>::error_event());
248 }
249 else
250 {
251 if (!traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event(watch, event))
252 throw std::runtime_error("Insufficient memory while handling watch event");
253
254 restart();
255 }
256 }
257
258 boost::asio::io_service& io_service;
259 boost::asio::posix::stream_descriptor stream_descriptor;
260 UnderlyingWatchType* watch;
261 };
262
263 template<typename T>
264 struct Holder
265 {
266 static void ptr_delete(void* p)
267 {
268 delete static_cast<Holder<T>*>(p);
269 }
270
271 Holder(const T& t) : value(t)
272 {
273 }
274
275 T value;
276 };
277
278 static dbus_bool_t on_dbus_add_watch(DBusWatch* watch, void* data)
279 {
280 if (dbus_watch_get_enabled(watch) == FALSE)
281 return TRUE;
282
283 auto thiz = static_cast<Executor*>(data);
284 auto w = std::shared_ptr<Watch<>>(new Watch<>(thiz->io_service, watch));
285 auto holder = new Holder<std::shared_ptr<Watch<>>>(w);
286 dbus_watch_set_data(watch, holder, Holder<std::shared_ptr<Watch<>>>::ptr_delete);
287
288 w->start();
289
290 return TRUE;
291 }
292
293 static void on_dbus_remove_watch(DBusWatch* watch, void*)
294 {
295 auto w = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch));
296 if (!w)
297 return;
298 w->value->cancel();
299 }
300
301 static void on_dbus_watch_toggled(DBusWatch* watch, void*)
302 {
303 auto holder = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch));
304 if (!holder)
305 return;
306 dbus_watch_get_enabled(watch) == TRUE ? holder->value->restart() : holder->value->cancel();
307 }
308
309 static dbus_bool_t on_dbus_add_timeout(DBusTimeout* timeout, void* data)
310 {
311 auto thiz = static_cast<Executor*>(data);
312 auto t = std::shared_ptr<Timeout<>>(new Timeout<>(thiz->io_service, timeout));
313 auto holder = new Holder<std::shared_ptr<Timeout<>>>(t);
314 dbus_timeout_set_data(
315 timeout,
316 holder,
317 Holder<std::shared_ptr<Timeout<>>>::ptr_delete);
318
319 t->start();
320 return TRUE;
321 }
322
323 static void on_dbus_remove_timeout(DBusTimeout* timeout, void*)
324 {
325 static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout))->value->cancel();
326 }
327
328 static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*)
329 {
330 auto holder = static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout));
331 holder->value->start();
332 }
333
334 static void on_dbus_wakeup_event_loop(void* data)
335 {
336 auto thiz = static_cast<Executor*>(data);
337 auto bus = thiz->bus;
338 thiz->io_service.post([bus]()
339 {
340 while (dbus_connection_get_dispatch_status(bus->raw()) == DBUS_DISPATCH_DATA_REMAINS)
341 {
342 dbus_connection_dispatch(bus->raw());
343 }
344 });
345 }
346
347public:
348
349 Executor(const Bus::Ptr& bus, boost::asio::io_service& io) : bus(bus), io_service(io), work(io_service)
350 {
351 if (!bus)
352 throw std::runtime_error("Precondition violated, cannot construct executor for null bus.");
353
354 if (!dbus_connection_set_watch_functions(
355 bus->raw(),
356 on_dbus_add_watch,
357 on_dbus_remove_watch,
358 on_dbus_watch_toggled,
359 this,
360 nullptr))
361 throw std::runtime_error("Problem installing watch functions.");
362
363 if (!dbus_connection_set_timeout_functions(
364 bus->raw(),
365 on_dbus_add_timeout,
366 on_dbus_remove_timeout,
367 on_dbus_timeout_toggled,
368 this,
369 nullptr))
370 throw std::runtime_error("Problem installing timeout functions.");
371
372 dbus_connection_set_wakeup_main_function(
373 bus->raw(),
374 on_dbus_wakeup_event_loop,
375 this,
376 nullptr);
377 }
378
379 ~Executor() noexcept
380 {
381 stop();
382 }
383
384 void run()
385 {
386 io_service.run();
387 }
388
389 void stop()
390 {
391 io_service.stop();
392 }
393
394private:
395 Bus::Ptr bus;
396 boost::asio::io_service& io_service;
397 boost::asio::io_service::work work;
398};
399
400ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus)
401{
402 static boost::asio::io_service io;
403 return std::make_shared<core::dbus::asio::Executor>(bus, io);
404}
405
406ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus, boost::asio::io_service& io)
407{
408 return std::make_shared<core::dbus::asio::Executor>(bus, io);
409}
410
411}
412}
413}
414
415204
=== added file 'src/core/dbus/asio/asio.h'
--- src/core/dbus/asio/asio.h 1970-01-01 00:00:00 +0000
+++ src/core/dbus/asio/asio.h 2016-01-18 16:49:39 +0000
@@ -0,0 +1,108 @@
1/*
2 * Copyright © 2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#ifndef CORE_DBUS_ASIO_EXECUTOR_H_
20#define CORE_DBUS_ASIO_EXECUTOR_H_
21
22#include <core/dbus/bus.h>
23#include <core/dbus/executor.h>
24#include <core/dbus/visibility.h>
25
26#include <boost/asio.hpp>
27
28#include <dbus/dbus.h>
29
30namespace core
31{
32namespace dbus
33{
34namespace asio
35{
36class ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor : public core::dbus::Executor
37{
38public:
39 typedef std::shared_ptr<Executor> Ptr;
40
41 class Timeout : public std::enable_shared_from_this<Timeout>, public core::dbus::Executor::Timeout
42 {
43 public:
44 typedef std::shared_ptr<Timeout> Ptr;
45
46 static Ptr create(const std::shared_ptr<boost::asio::io_service>& io_service, const std::chrono::milliseconds& period);
47
48 ~Timeout();
49
50 void start() override;
51 void stop() override;
52 const core::Signal<void>& on_triggered() override;
53
54 private:
55 Timeout(const std::shared_ptr<boost::asio::io_service>& io_service, const std::chrono::milliseconds& period);
56 void on_timeout(const boost::system::error_code& ec);
57
58 std::shared_ptr<boost::asio::io_service> ios;
59 core::Signal<void> triggered;
60 boost::asio::deadline_timer timer;
61 std::chrono::milliseconds period;
62 };
63
64 class Watch : public std::enable_shared_from_this<Watch>,
65 public core::dbus::Executor::Watch
66 {
67 public:
68 typedef std::shared_ptr<Watch> Ptr;
69 static Ptr create(const std::shared_ptr<boost::asio::io_service>& io_service, int fd);
70
71 ~Watch() noexcept(true);
72
73 void start_with_flags(Watch::Flags flags) override;
74 void stop() override;
75 const core::Signal<Flags>& on_triggered() override;
76
77 private:
78 Watch(const std::shared_ptr<boost::asio::io_service>& io_service, int fd);
79 void restart();
80 void on_stream_descriptor_event(Watch::Flags flags, Watch::Flags requested, std::size_t);
81
82 std::shared_ptr<boost::asio::io_service> ios;
83 core::Signal<Watch::Flags> triggered;
84 boost::asio::posix::stream_descriptor stream_descriptor;
85 };
86
87public:
88 Executor(const std::shared_ptr<boost::asio::io_service>& ios);
89
90 ~Executor() noexcept(true);
91
92 core::dbus::Executor::Watch::Ptr add_watch(int fd) override;
93 core::dbus::Executor::Timeout::Ptr add_timeout(const std::chrono::milliseconds& duration) override;
94 void dispatch(const Task& task) override;
95
96 void run() override;
97 void stop() override;
98
99private:
100 std::shared_ptr<boost::asio::io_service> ios;
101 boost::asio::io_service::work work;
102};
103
104}
105}
106}
107
108#endif // CORE_DBUS_ASIO_EXECUTOR_H_
0109
=== modified file 'src/core/dbus/bus.cpp'
--- src/core/dbus/bus.cpp 2016-01-02 22:02:10 +0000
+++ src/core/dbus/bus.cpp 2016-01-18 16:49:39 +0000
@@ -21,8 +21,7 @@
21#include <core/dbus/match_rule.h>21#include <core/dbus/match_rule.h>
22#include <core/dbus/object.h>22#include <core/dbus/object.h>
2323
24#include <core/dbus/traits/timeout.h>24#include <dbus/dbus.h>
25#include <core/dbus/traits/watch.h>
2625
27#include <core/posix/this_process.h>26#include <core/posix/this_process.h>
2827
@@ -32,6 +31,112 @@
3231
33namespace32namespace
34{33{
34namespace timeout
35{
36bool is_enabled(DBusTimeout* timeout)
37{
38 return TRUE == dbus_timeout_get_enabled(timeout);
39}
40
41std::chrono::milliseconds get_interval(DBusTimeout* timeout)
42{
43 return std::chrono::milliseconds{dbus_timeout_get_interval(timeout)};
44}
45
46void set_data(DBusTimeout* timeout, void* data, void (*deleter)(void*))
47{
48 dbus_timeout_set_data(timeout, data, deleter);
49}
50
51void invoke_handler_or_throw(DBusTimeout* timeout)
52{
53 if (not dbus_timeout_handle(timeout))
54 throw std::runtime_error{"Error invoking dbus timeout handler"};
55}
56}
57
58namespace watch
59{
60constexpr int readable_event()
61{
62 return DBUS_WATCH_READABLE;
63}
64
65constexpr int writeable_event()
66{
67 return DBUS_WATCH_WRITABLE;
68}
69
70constexpr int error_event()
71{
72 return DBUS_WATCH_ERROR;
73}
74
75constexpr int hangup_event()
76{
77 return DBUS_WATCH_HANGUP;
78}
79
80bool is_watch_enabled(DBusWatch* watch)
81{
82 return TRUE == dbus_watch_get_enabled(watch);
83}
84
85int get_unix_fd(DBusWatch* watch)
86{
87 return dbus_watch_get_unix_fd(watch);
88}
89
90bool is_monitoring_fd_for_readable(DBusWatch* watch)
91{
92 return dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE;
93}
94
95bool is_monitoring_fd_for_writable(DBusWatch* watch)
96{
97 return dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE;
98}
99
100void* get_data(DBusWatch* watch)
101{
102 return dbus_watch_get_data(watch);
103}
104
105void set_data(DBusWatch* watch, void* data, void (*deleter)(void*))
106{
107 dbus_watch_set_data(watch, data, deleter);
108}
109
110void invoke_handler_or_throw(DBusWatch* watch, int event)
111{
112 if (not dbus_watch_handle(watch, event))
113 {
114 throw std::runtime_error{"Error invoking DBus watch handler."};
115 }
116}
117}
118
119template<typename T>
120struct Holder
121{
122 static void ptr_delete(void* p)
123 {
124 delete static_cast<T*>(p);
125 }
126
127 Holder(const T& t) : value(t)
128 {
129 }
130
131 T value;
132};
133
134template<typename T>
135Holder<T>* make_holder(const T& value)
136{
137 return new Holder<T>(value);
138}
139
35struct VTable140struct VTable
36{141{
37 static void unregister_object_path(DBusConnection*, void* data)142 static void unregister_object_path(DBusConnection*, void* data)
@@ -127,6 +232,126 @@
127 init_libdbus_thread_support_and_install_shutdown_handler();232 init_libdbus_thread_support_and_install_shutdown_handler();
128 }233 }
129234
235 static dbus_bool_t on_dbus_add_watch(DBusWatch* watch, void* data)
236 {
237 auto thiz = static_cast<Private*>(data);
238
239 if (not thiz)
240 return FALSE;
241
242 Executor::Watch::Flags f{Executor::Watch::Flags::none};
243
244 if (watch::is_monitoring_fd_for_readable(watch))
245 f = f | Executor::Watch::Flags::readable;
246 if (watch::is_monitoring_fd_for_writable(watch))
247 f = f | Executor::Watch::Flags::writable;
248
249 auto w = thiz->executor->add_watch(watch::get_unix_fd(watch));
250
251 w->on_triggered().connect([watch](Executor::Watch::Flags flags)
252 {
253 int event = 0;
254
255 if ((flags & Executor::Watch::Flags::readable) == Executor::Watch::Flags::readable)
256 event |= watch::readable_event();
257 if ((flags & Executor::Watch::Flags::writable) == Executor::Watch::Flags::writable)
258 event |= watch::writeable_event();
259 if ((flags & Executor::Watch::Flags::error) == Executor::Watch::Flags::error)
260 event |= watch::error_event();
261 if ((flags & Executor::Watch::Flags::hangup) == Executor::Watch::Flags::hangup)
262 event |= watch::hangup_event();
263
264 watch::invoke_handler_or_throw(watch, event);
265 });
266
267 watch::set_data(watch, make_holder(w), Holder<Executor::Watch::Ptr>::ptr_delete);
268
269 if (watch::is_watch_enabled(watch))
270 w->start_with_flags(f);
271
272 return TRUE;
273 }
274
275 static void on_dbus_remove_watch(DBusWatch* watch, void*)
276 {
277 if (auto w = static_cast<Holder<Executor::Watch::Ptr>*>(watch::get_data(watch)))
278 w->value->stop();
279 }
280
281 static void on_dbus_watch_toggled(DBusWatch* watch, void*)
282 {
283 if (auto holder = static_cast<Holder<Executor::Watch::Ptr>*>(watch::get_data(watch)))
284 {
285 Executor::Watch::Flags f{Executor::Watch::Flags::none};
286
287 if (watch::is_monitoring_fd_for_readable(watch))
288 f = f | Executor::Watch::Flags::readable;
289 if (watch::is_monitoring_fd_for_writable(watch))
290 f = f | Executor::Watch::Flags::writable;
291
292 if (watch::is_watch_enabled(watch))
293 holder->value->start_with_flags(f);
294 else
295 holder->value->stop();
296 }
297 }
298
299 static dbus_bool_t on_dbus_add_timeout(DBusTimeout* timeout, void* data)
300 {
301 auto thiz = static_cast<Private*>(data);
302
303 if (not thiz)
304 return FALSE;
305
306 const auto interval = timeout::get_interval(timeout);
307
308 auto t = thiz->executor->add_timeout(interval);
309
310 t->on_triggered().connect([timeout]()
311 {
312 timeout::invoke_handler_or_throw(timeout);
313 });
314
315 timeout::set_data(timeout, make_holder(t), Holder<Executor::Timeout::Ptr>::ptr_delete);
316
317 if (timeout::is_enabled(timeout))
318 t->start();
319
320 return TRUE;
321 }
322
323 static void on_dbus_remove_timeout(DBusTimeout* timeout, void*)
324 {
325 if (auto holder = static_cast<Holder<Executor::Timeout::Ptr>*>(dbus_timeout_get_data(timeout)))
326 holder->value->stop();
327 }
328
329 static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*)
330 {
331 if (auto holder = static_cast<Holder<Executor::Timeout::Ptr>*>(dbus_timeout_get_data(timeout)))
332 {
333 if (timeout::is_enabled(timeout))
334 holder->value->start();
335 else
336 holder->value->stop();
337 }
338
339 }
340
341 static void on_dbus_wakeup_event_loop(void* data)
342 {
343 auto thiz = static_cast<Private*>(data);
344 auto connection = thiz->connection;
345
346 thiz->executor->dispatch([connection]()
347 {
348 while (dbus_connection_get_dispatch_status(connection.get()) == DBUS_DISPATCH_DATA_REMAINS)
349 {
350 dbus_connection_dispatch(connection.get());
351 }
352 });
353 }
354
130 std::shared_ptr<DBusConnection> connection;355 std::shared_ptr<DBusConnection> connection;
131 std::shared_ptr<MessageFactory> message_factory_impl;356 std::shared_ptr<MessageFactory> message_factory_impl;
132 Executor::Ptr executor;357 Executor::Ptr executor;
@@ -339,6 +564,30 @@
339void Bus::install_executor(const Executor::Ptr& e)564void Bus::install_executor(const Executor::Ptr& e)
340{565{
341 d->executor = e;566 d->executor = e;
567
568 if (!dbus_connection_set_watch_functions(
569 d->connection.get(),
570 Private::on_dbus_add_watch,
571 Private::on_dbus_remove_watch,
572 Private::on_dbus_watch_toggled,
573 d.get(),
574 nullptr))
575 throw std::runtime_error("Problem installing watch functions.");
576
577 if (!dbus_connection_set_timeout_functions(
578 d->connection.get(),
579 Private::on_dbus_add_timeout,
580 Private::on_dbus_remove_timeout,
581 Private::on_dbus_timeout_toggled,
582 d.get(),
583 nullptr))
584 throw std::runtime_error("Problem installing timeout functions.");
585
586 dbus_connection_set_wakeup_main_function(
587 d->connection.get(),
588 Private::on_dbus_wakeup_event_loop,
589 d.get(),
590 nullptr);
342}591}
343592
344void Bus::stop()593void Bus::stop()
345594
=== added file 'src/core/dbus/executor.cpp'
--- src/core/dbus/executor.cpp 1970-01-01 00:00:00 +0000
+++ src/core/dbus/executor.cpp 2016-01-18 16:49:39 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright © 2012 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#include <core/dbus/executor.h>
20
21#include <bitset>
22#include <iostream>
23
24namespace
25{
26using NT = typename std::underlying_type<core::dbus::Executor::Watch::Flags>::type;
27}
28
29std::ostream& core::dbus::operator<<(std::ostream& out, core::dbus::Executor::Watch::Flags flags)
30{
31 return out << "Watch::Flags[" << std::bitset<4*sizeof(NT)>(static_cast<NT>(flags)).to_string() << "]";
32}
33
34core::dbus::Executor::Watch::Flags core::dbus::operator|(core::dbus::Executor::Watch::Flags lhs, core::dbus::Executor::Watch::Flags rhs)
35{
36 return static_cast<core::dbus::Executor::Watch::Flags>(static_cast<NT>(lhs) | static_cast<NT>(rhs));
37}
38
39core::dbus::Executor::Watch::Flags core::dbus::operator&(core::dbus::Executor::Watch::Flags lhs, core::dbus::Executor::Watch::Flags rhs)
40{
41 return static_cast<core::dbus::Executor::Watch::Flags>(static_cast<NT>(lhs) & static_cast<NT>(rhs));
42}
043
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2014-09-28 21:27:09 +0000
+++ tests/CMakeLists.txt 2016-01-18 16:49:39 +0000
@@ -31,6 +31,7 @@
31 ${CMAKE_CURRENT_BINARY_DIR}/test_data.h @ONLY)31 ${CMAKE_CURRENT_BINARY_DIR}/test_data.h @ONLY)
3232
33include_directories(33include_directories(
34 ${CMAKE_SOURCE_DIR}/src
34 ${CMAKE_CURRENT_BINARY_DIR}35 ${CMAKE_CURRENT_BINARY_DIR}
35)36)
3637
3738
=== modified file 'tests/executor_test.cpp'
--- tests/executor_test.cpp 2014-09-28 21:27:09 +0000
+++ tests/executor_test.cpp 2016-01-18 16:49:39 +0000
@@ -16,7 +16,8 @@
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */17 */
1818
19#include <core/dbus/asio/executor.h>19#include <core/dbus/asio/make_executor.h>
20#include <core/dbus/asio/asio.h>
2021
21#include <core/dbus/dbus.h>22#include <core/dbus/dbus.h>
22#include <core/dbus/fixture.h>23#include <core/dbus/fixture.h>
@@ -53,10 +54,15 @@
53 std::binomial_distribution<> coin{1, 1.-probability_for_failure};54 std::binomial_distribution<> coin{1, 1.-probability_for_failure};
54};55};
5556
57struct ExecutorWatchFlags : public ::testing::TestWithParam<core::dbus::Executor::Watch::Flags>
58{
59
60};
61
56struct Executor : public core::dbus::testing::Fixture62struct Executor : public core::dbus::testing::Fixture
57{63{
58 protected:64 protected:
59 boost::asio::io_service io_service;65 std::shared_ptr<boost::asio::io_service> io_service = std::make_shared<boost::asio::io_service>();
60};66};
6167
62auto session_bus_config_file =68auto session_bus_config_file =
@@ -68,16 +74,119 @@
68 core::testing::system_bus_configuration_file();74 core::testing::system_bus_configuration_file();
69}75}
7076
71TEST_F(Executor, ThrowsOnConstructionFromNullBus)77TEST_P(ExecutorWatchFlags, BinaryOrAndWorksAsExpected)
72{78{
73 EXPECT_ANY_THROW(core::dbus::asio::make_executor(core::dbus::Bus::Ptr{}, io_service));79 core::dbus::Executor::Watch::Flags f{core::dbus::Executor::Watch::Flags::none};
74}80 EXPECT_EQ(GetParam(), (f | GetParam()) & GetParam());
7581}
76TEST_F(Executor, DoesNotThrowForExistingBus)82
77{83INSTANTIATE_TEST_CASE_P(ExecutorWatchFlags,
78 auto bus = session_bus();84 ExecutorWatchFlags,
79 EXPECT_NO_THROW(bus->install_executor(core::dbus::asio::make_executor(bus, io_service)));85 ::testing::Values(core::dbus::Executor::Watch::Flags::readable,
80}86 core::dbus::Executor::Watch::Flags::writable,
87 core::dbus::Executor::Watch::Flags::error,
88 core::dbus::Executor::Watch::Flags::hangup));
89
90TEST(ExecutorWatch, TriggersForFdReadable)
91{
92 static constexpr const std::size_t read = 0;
93 static constexpr const std::size_t write = 1;
94
95 int fds[2];
96 ::pipe(fds);
97
98 auto ios = std::make_shared<boost::asio::io_service>();
99 boost::asio::io_service::work work{*ios};
100
101 auto watch = core::dbus::asio::Executor::Watch::create(ios, fds[read]);
102 watch->on_triggered().connect([ios](core::dbus::asio::Executor::Watch::Flags flags)
103 {
104 EXPECT_EQ(core::dbus::asio::Executor::Watch::Flags::readable, flags & core::dbus::asio::Executor::Watch::Flags::readable);
105 ios->stop();
106 });
107 watch->start_with_flags(core::dbus::asio::Executor::Watch::Flags::readable);
108
109 ::write(fds[write], &read, sizeof(read));
110
111 ios->run();
112
113 ::close(fds[read]); ::close(fds[write]);
114}
115
116TEST(ExecutorWatch, TriggersForFdWritable)
117{
118 static constexpr const std::size_t read = 0;
119 static constexpr const std::size_t write = 1;
120
121 int fds[2];
122 ::pipe(fds);
123
124 auto ios = std::make_shared<boost::asio::io_service>();
125 boost::asio::io_service::work work{*ios};
126
127 auto ww = core::dbus::asio::Executor::Watch::create(ios, fds[write]);
128 ww->on_triggered().connect([ios](core::dbus::asio::Executor::Watch::Flags flags)
129 {
130 EXPECT_EQ(core::dbus::asio::Executor::Watch::Flags::writable, flags & core::dbus::asio::Executor::Watch::Flags::writable);
131 ios->stop();
132 });
133 ww->start_with_flags(core::dbus::asio::Executor::Watch::Flags::writable);
134
135 std::size_t v;
136 ::write(fds[write], &v, sizeof(v));
137
138 ios->run();
139
140 ::close(fds[read]); ::close(fds[write]);
141}
142
143TEST(ExecutorTimeout, TimeoutTriggersCorrectly)
144{
145 auto ios = std::make_shared<boost::asio::io_service>();
146 boost::asio::io_service::work work{*ios};
147
148 auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500});
149 to->on_triggered().connect([ios]()
150 {
151 ios->stop();
152 });
153
154 to->start();
155 ios->run();
156}
157
158TEST(ExecutorTimeout, TimeoutTriggersCorrectlyForZeroDuration)
159{
160 auto ios = std::make_shared<boost::asio::io_service>();
161 boost::asio::io_service::work work{*ios};
162
163 auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{0});
164 to->on_triggered().connect([ios]()
165 {
166 ios->stop();
167 });
168
169 to->start();
170 ios->run();
171}
172
173TEST(ExecutorTimeout, TimeoutGoingOutOfScopeDoesNotSegfault)
174{
175 auto ios = std::make_shared<boost::asio::io_service>();
176 boost::asio::io_service::work work{*ios};
177
178 {
179 auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500});
180 to->start();
181 }
182
183 auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500});
184 to->on_triggered().connect([ios]() { ios->stop(); });
185 to->start();
186
187 ios->run();
188}
189
81190
82TEST_F(Executor, ABusRunByAnExecutorReceivesSignals)191TEST_F(Executor, ABusRunByAnExecutorReceivesSignals)
83{192{
@@ -206,45 +315,30 @@
206 auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<test::Service>::interface_name());315 auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<test::Service>::interface_name());
207 auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));316 auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));
208317
209 std::thread updater1([bus, stub]()318 auto updater = [](const core::dbus::Object::Ptr& stub)
210 {319 {
211 for (unsigned int counter = 0; counter < 1000; counter++)320 for (unsigned int counter = 0; counter < 1000; counter++)
212 {321 {
213 try322 try
214 {323 {
215 auto result = stub->transact_method<test::Service::Method, int64_t>();324 auto result = stub->transact_method<test::Service::Method, int64_t>();
216325
217 if (result.is_error())326 if (not result.is_error())
218 std::cout << result.error().print() << std::endl;327 {
219 else328 EXPECT_EQ(42, result.value());
220 EXPECT_EQ(42, result.value());329 }
221330
222 } catch(const std::runtime_error& e)331 } catch(...)
223 {332 {
224 std::cout << e.what() << std::endl;333 // Dropping the exception on purpose. We have a chaos monkey running,
225 }334 //
226 }335 }
227 });336 }
228337 };
229 std::thread updater2([bus, stub]()338
230 {339
231 for (unsigned int counter = 0; counter < 1000; counter++)340 std::thread updater1(updater, stub);
232 {341 std::thread updater2(updater, stub);
233 try
234 {
235 auto result = stub->transact_method<test::Service::Method, int64_t>();
236
237 if (result.is_error())
238 std::cout << result.error().print() << std::endl;
239 else
240 EXPECT_EQ(42, result.value());
241
242 } catch(const std::runtime_error& e)
243 {
244 std::cout << e.what() << std::endl;
245 }
246 }
247 });
248342
249 if (updater1.joinable())343 if (updater1.joinable())
250 updater1.join();344 updater1.join();
251345
=== modified file 'tests/service_test.cpp'
--- tests/service_test.cpp 2014-07-30 16:12:16 +0000
+++ tests/service_test.cpp 2016-01-18 16:49:39 +0000
@@ -66,11 +66,13 @@
6666
67TEST_F(Service, AddingServiceAndObjectAndCallingIntoItSucceeds)67TEST_F(Service, AddingServiceAndObjectAndCallingIntoItSucceeds)
68{68{
69 core::testing::CrossProcessSync cps1;69 core::testing::CrossProcessSync cps1;
7070
71 const int64_t expected_value = 42;71 const int64_t expected_value = 42;
7272
73 auto service = [this, expected_value, &cps1]()73 auto service = [this, expected_value, &cps1]()
74 {
75 try
74 {76 {
75 core::testing::SigTermCatcher sc;77 core::testing::SigTermCatcher sc;
7678
@@ -87,6 +89,7 @@
8789
88 skeleton->install_method_handler<test::Service::Method>([bus, skeleton, &readonly_property, expected_value](const dbus::Message::Ptr& msg)90 skeleton->install_method_handler<test::Service::Method>([bus, skeleton, &readonly_property, expected_value](const dbus::Message::Ptr& msg)
89 {91 {
92 std::cout << "here" << std::endl;
90 auto reply = dbus::Message::make_method_return(msg);93 auto reply = dbus::Message::make_method_return(msg);
91 reply->writer() << expected_value;94 reply->writer() << expected_value;
92 bus->send(reply);95 bus->send(reply);
@@ -95,9 +98,9 @@
95 auto changed_signal = skeleton->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>();98 auto changed_signal = skeleton->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>();
96 core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType99 core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
97 args("this.is.unlikely.to.exist.Service",100 args("this.is.unlikely.to.exist.Service",
98 {{test::Service::Properties::ReadOnly::name(),101 {{test::Service::Properties::ReadOnly::name(),
99 core::dbus::types::TypedVariant<test::Service::Properties::ReadOnly::ValueType>(expected_value)}},102 core::dbus::types::TypedVariant<test::Service::Properties::ReadOnly::ValueType>(expected_value)}},
100 {});103 {});
101 skeleton->emit_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged, core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType>(args);104 skeleton->emit_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged, core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType>(args);
102 changed_signal->emit(args);105 changed_signal->emit(args);
103106
@@ -115,15 +118,22 @@
115 t.join();118 t.join();
116119
117 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;120 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
118 };121 } catch(const std::exception& e)
119122 {
120 auto client = [this, expected_value, &cps1]()123 std::cout << e.what() << std::endl;
124 }
125
126 return core::posix::exit::Status::failure;
127 };
128
129 auto client = [this, expected_value, &cps1]()
130 {
131 try
121 {132 {
122 auto bus = session_bus();133 auto bus = session_bus();
123 bus->install_executor(core::dbus::asio::make_executor(bus));134 bus->install_executor(core::dbus::asio::make_executor(bus));
124 std::thread t{[bus](){ bus->run(); }};135 std::thread t{[bus](){ bus->run(); }};
125 EXPECT_EQ(std::uint32_t(1), cps1.wait_for_signal_ready_for(std::chrono::milliseconds{500}));136 EXPECT_EQ(std::uint32_t(1), cps1.wait_for_signal_ready_for(std::chrono::milliseconds{500}));
126
127 auto stub_service = dbus::Service::use_service<test::Service>(bus);137 auto stub_service = dbus::Service::use_service<test::Service>(bus);
128 auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));138 auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service"));
129 EXPECT_EQ(stub->path().as_string(), "/this/is/unlikely/to/exist/Service");139 EXPECT_EQ(stub->path().as_string(), "/this/is/unlikely/to/exist/Service");
@@ -151,6 +161,7 @@
151 try161 try
152 {162 {
153 auto result = stub->invoke_method_synchronously<test::Service::Method, int64_t>();163 auto result = stub->invoke_method_synchronously<test::Service::Method, int64_t>();
164 std::cout << "here" << std::endl;
154 EXPECT_FALSE(result.is_error());165 EXPECT_FALSE(result.is_error());
155 EXPECT_EQ(expected_value, result.value());166 EXPECT_EQ(expected_value, result.value());
156 } catch(const std::exception& e)167 } catch(const std::exception& e)
@@ -170,9 +181,15 @@
170 EXPECT_EQ(changed_value, expected_value);181 EXPECT_EQ(changed_value, expected_value);
171182
172 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;183 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
173 };184 }
185 catch (const std::exception& e)
186 {
187 std::cout << e.what() << std::endl;
188 }
189 return core::posix::exit::Status::failure;
190 };
174191
175 EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));192 EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client));
176}193}
177194
178TEST_F(Service, AddingANonExistingServiceDoesNotThrow)195TEST_F(Service, AddingANonExistingServiceDoesNotThrow)

Subscribers

People subscribed via source and target branches