Merge lp:~thomas-voss/dbus-cpp/decouple-executor-and-bus into lp:dbus-cpp/15.04
- decouple-executor-and-bus
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+282977@code.launchpad.net |
Commit message
Decouple dbus::Executor/
Description of the change
Decouple dbus::Executor/
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
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-11-10 08:35:29 +0000 |
3 | +++ CMakeLists.txt 2016-01-18 16:49:39 +0000 |
4 | @@ -67,6 +67,8 @@ |
5 | |
6 | include(CTest) |
7 | |
8 | +file(GLOB_RECURSE DBUS_CPP_INTERFACE_HEADERS include/*.h) |
9 | + |
10 | include_directories( |
11 | include |
12 | ${LIBXML2_INCLUDE_DIR} |
13 | |
14 | === renamed file 'include/core/dbus/asio/executor.h' => 'include/core/dbus/asio/make_executor.h' |
15 | --- include/core/dbus/asio/executor.h 2014-06-09 08:56:57 +0000 |
16 | +++ include/core/dbus/asio/make_executor.h 2016-01-18 16:49:39 +0000 |
17 | @@ -15,8 +15,8 @@ |
18 | * |
19 | * Authored by: Thomas Voß <thomas.voss@canonical.com> |
20 | */ |
21 | -#ifndef CORE_DBUS_ASIO_EXECUTOR_H_ |
22 | -#define CORE_DBUS_ASIO_EXECUTOR_H_ |
23 | +#ifndef CORE_DBUS_ASIO_MAKE_EXECUTOR_H_ |
24 | +#define CORE_DBUS_ASIO_MAKE_EXECUTOR_H_ |
25 | |
26 | #include <core/dbus/bus.h> |
27 | #include <core/dbus/executor.h> |
28 | @@ -36,10 +36,10 @@ |
29 | { |
30 | namespace asio |
31 | { |
32 | -ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus); |
33 | -ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus, boost::asio::io_service& io); |
34 | +ORG_FREEDESKTOP_DBUS_DLL_PUBLIC core::dbus::Executor::Ptr make_executor(const Bus::Ptr& bus); |
35 | +ORG_FREEDESKTOP_DBUS_DLL_PUBLIC core::dbus::Executor::Ptr make_executor(const Bus::Ptr& bus, const std::shared_ptr<boost::asio::io_service>& io); |
36 | } |
37 | } |
38 | } |
39 | |
40 | -#endif // CORE_DBUS_ASIO_EXECUTOR_H_ |
41 | +#endif // CORE_DBUS_ASIO_MAKE_EXECUTOR_H_ |
42 | |
43 | === modified file 'include/core/dbus/executor.h' |
44 | --- include/core/dbus/executor.h 2013-11-27 18:57:42 +0000 |
45 | +++ include/core/dbus/executor.h 2016-01-18 16:49:39 +0000 |
46 | @@ -20,6 +20,10 @@ |
47 | |
48 | #include <core/dbus/visibility.h> |
49 | |
50 | +#include <core/signal.h> |
51 | + |
52 | +#include <functional> |
53 | +#include <iosfwd> |
54 | #include <memory> |
55 | |
56 | namespace core |
57 | @@ -34,25 +38,97 @@ |
58 | public: |
59 | typedef std::shared_ptr<Executor> Ptr; |
60 | |
61 | + /** @brief Task models an arbitrary function to be executed by an Executor. */ |
62 | + typedef std::function<void()> Task; |
63 | + |
64 | + /** @brief A Timeout models an individual recurring timer. */ |
65 | + class Timeout |
66 | + { |
67 | + public: |
68 | + /** @cond */ |
69 | + typedef std::shared_ptr<Timeout> Ptr; |
70 | + |
71 | + Timeout(Timeout&&) = delete; |
72 | + Timeout(const Timeout&) = delete; |
73 | + Timeout&& operator=(Timeout&&) = delete; |
74 | + Timeout& operator=(const Timeout&) = delete; |
75 | + virtual ~Timeout() = default; |
76 | + /** @endcond */ |
77 | + |
78 | + /** @brief start enables the timer. */ |
79 | + virtual void start() = 0; |
80 | + /** @brief stop stops the timer. */ |
81 | + virtual void stop() = 0; |
82 | + /** @brief triggered returns a signal that is emitted whenever the timer expires. */ |
83 | + virtual const core::Signal<void>& on_triggered() = 0; |
84 | + |
85 | + protected: |
86 | + Timeout() = default; |
87 | + }; |
88 | + |
89 | + /** @brief A Timeout models a recurring timer. */ |
90 | + class Watch |
91 | + { |
92 | + public: |
93 | + /** @cond */ |
94 | + typedef std::shared_ptr<Watch> Ptr; |
95 | + /** @endcond */ |
96 | + |
97 | + /** @brief Flags enumerates all possible operations and result codes when watching file descriptors. */ |
98 | + enum class Flags : std::uint8_t |
99 | + { |
100 | + none = 0, |
101 | + readable = 1 << 0, |
102 | + writable = 1 << 1, |
103 | + error = 1 << 2, |
104 | + hangup = 1 << 3 |
105 | + }; |
106 | + |
107 | + /** @cond */ |
108 | + Watch(Watch&&) = delete; |
109 | + Watch(const Watch&) = delete; |
110 | + Watch&& operator=(Watch&&) = delete; |
111 | + Watch& operator=(const Watch&) = delete; |
112 | + virtual ~Watch() = default; |
113 | + /** @endcond */ |
114 | + |
115 | + /** @brief start enables the watch. */ |
116 | + virtual void start_with_flags(Watch::Flags flags) = 0; |
117 | + /** @brief stop stops the watch. */ |
118 | + virtual void stop() = 0; |
119 | + /** @brief triggered returns a signal that is emitted when the underlying fd becomes read/writable. */ |
120 | + virtual const core::Signal<Flags>& on_triggered() = 0; |
121 | + |
122 | + protected: |
123 | + Watch() = default; |
124 | + }; |
125 | + |
126 | virtual ~Executor() = default; |
127 | |
128 | + /** @brief add_watch returns a Watch instance monitoring fd. */ |
129 | + virtual Watch::Ptr add_watch(int fd) = 0; |
130 | + /** @brief add_timeout returns a Timeout instance. */ |
131 | + virtual Timeout::Ptr add_timeout(const std::chrono::milliseconds& duration) = 0; |
132 | + /** @brief dispatch takes task and executes it either now or later. */ |
133 | + virtual void dispatch(const Task& task) = 0; |
134 | + |
135 | + /** @brief Start the event loop and block until stop is called. */ |
136 | + virtual void run() = 0; |
137 | + |
138 | + /** @brief Stop the event loop. */ |
139 | + virtual void stop() = 0; |
140 | + |
141 | protected: |
142 | friend class Bus; |
143 | |
144 | Executor() = default; |
145 | Executor(const Executor&) = delete; |
146 | Executor& operator=(const Executor&) = delete; |
147 | - |
148 | - /** |
149 | - * @brief Start the event loop and block until stop is called. |
150 | - */ |
151 | - virtual void run() = 0; |
152 | - |
153 | - /** |
154 | - * @brief Stop the event loop. |
155 | - */ |
156 | - virtual void stop() = 0; |
157 | }; |
158 | + |
159 | +ORG_FREEDESKTOP_DBUS_DLL_PUBLIC std::ostream& operator<<(std::ostream& out, Executor::Watch::Flags flags); |
160 | +ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Watch::Flags operator|(Executor::Watch::Flags lhs, Executor::Watch::Flags rhs); |
161 | +ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Watch::Flags operator&(Executor::Watch::Flags lhs, Executor::Watch::Flags rhs); |
162 | } |
163 | } |
164 | |
165 | |
166 | === modified file 'src/core/dbus/CMakeLists.txt' |
167 | --- src/core/dbus/CMakeLists.txt 2014-03-05 15:02:12 +0000 |
168 | +++ src/core/dbus/CMakeLists.txt 2016-01-18 16:49:39 +0000 |
169 | @@ -21,6 +21,7 @@ |
170 | |
171 | include_directories( |
172 | ${CMAKE_SOURCE_DIR}/include |
173 | + ${CMAKE_SOURCE_DIR}/src |
174 | ${DBUS_INCLUDE_DIRS} |
175 | ${Boost_INCLUDE_DIRS} |
176 | ${PROCESS_CPP_INCLUDE_DIR} |
177 | @@ -33,17 +34,20 @@ |
178 | add_library( |
179 | dbus-cpp SHARED |
180 | |
181 | + ${DBUS_CPP_INTERFACE_HEADERS} |
182 | ${CMAKE_CURRENT_BINARY_DIR}/fixture.cpp |
183 | |
184 | bus.cpp |
185 | dbus.cpp |
186 | + executor.cpp |
187 | error.cpp |
188 | match_rule.cpp |
189 | message.cpp |
190 | service.cpp |
191 | service_watcher.cpp |
192 | |
193 | - asio/executor.cpp |
194 | + asio/asio.h |
195 | + asio/asio.cpp |
196 | |
197 | types/object_path.cpp |
198 | ) |
199 | |
200 | === renamed file 'src/core/dbus/asio/executor.cpp' => 'src/core/dbus/asio/asio.cpp' |
201 | --- src/core/dbus/asio/executor.cpp 2014-09-28 21:27:09 +0000 |
202 | +++ src/core/dbus/asio/asio.cpp 2016-01-18 16:49:39 +0000 |
203 | @@ -16,10 +16,9 @@ |
204 | * Authored by: Thomas Voß <thomas.voss@canonical.com> |
205 | */ |
206 | |
207 | -#include <core/dbus/bus.h> |
208 | #include <core/dbus/executor.h> |
209 | -#include <core/dbus/traits/timeout.h> |
210 | -#include <core/dbus/traits/watch.h> |
211 | +#include <core/dbus/asio/asio.h> |
212 | +#include <core/dbus/asio/make_executor.h> |
213 | |
214 | #include <boost/asio.hpp> |
215 | #include <boost/asio/io_service.hpp> |
216 | @@ -32,383 +31,173 @@ |
217 | #include <future> |
218 | #include <thread> |
219 | |
220 | -namespace core |
221 | -{ |
222 | -namespace dbus |
223 | -{ |
224 | -namespace traits |
225 | -{ |
226 | -template<> |
227 | -struct Timeout<DBusTimeout> |
228 | -{ |
229 | - typedef int DurationType; |
230 | - |
231 | - static inline bool is_timeout_enabled(DBusTimeout* timeout) |
232 | - { |
233 | - return TRUE == dbus_timeout_get_enabled(timeout); |
234 | - } |
235 | - |
236 | - static inline int get_timeout_interval(DBusTimeout* timeout) |
237 | - { |
238 | - return DurationType(dbus_timeout_get_interval(timeout)); |
239 | - } |
240 | - |
241 | - static inline void invoke_timeout_handler(DBusTimeout* timeout) |
242 | - { |
243 | - dbus_timeout_handle(timeout); |
244 | - } |
245 | -}; |
246 | - |
247 | -template<> |
248 | -struct Watch<DBusWatch> |
249 | -{ |
250 | - inline static int readable_event() { return DBUS_WATCH_READABLE; } |
251 | - inline static int writeable_event() { return DBUS_WATCH_WRITABLE; } |
252 | - inline static int error_event() { return DBUS_WATCH_ERROR; } |
253 | - inline static int hangup_event() { return DBUS_WATCH_HANGUP; } |
254 | - |
255 | - static inline bool is_watch_enabled(DBusWatch* watch) |
256 | - { |
257 | - return TRUE == dbus_watch_get_enabled(watch); |
258 | - } |
259 | - |
260 | - static inline int get_watch_unix_fd(DBusWatch* watch) |
261 | - { |
262 | - return dbus_watch_get_unix_fd(watch); |
263 | - } |
264 | - |
265 | - static inline bool is_watch_monitoring_fd_for_readable(DBusWatch* watch) |
266 | - { |
267 | - return dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE; |
268 | - } |
269 | - |
270 | - static bool is_watch_monitoring_fd_for_writable(DBusWatch* watch) |
271 | - { |
272 | - return dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE; |
273 | - } |
274 | - |
275 | - static bool invoke_watch_handler_for_event(DBusWatch* watch, int event) |
276 | - { |
277 | - return dbus_watch_handle(watch, event); |
278 | - } |
279 | -}; |
280 | -} |
281 | -namespace asio |
282 | -{ |
283 | -class Executor : public core::dbus::Executor |
284 | -{ |
285 | -public: |
286 | - template<typename UnderlyingTimeoutType = DBusTimeout> |
287 | - struct Timeout : std::enable_shared_from_this<Timeout<UnderlyingTimeoutType>> |
288 | - { |
289 | - Timeout(boost::asio::io_service& io_service, UnderlyingTimeoutType* timeout) |
290 | - : io_service(io_service), |
291 | - timer(io_service), |
292 | - timeout(timeout) |
293 | - { |
294 | - if (!timeout) |
295 | - throw std::runtime_error("Precondition violated: timeout has to be non-null"); |
296 | - } |
297 | - |
298 | - ~Timeout() |
299 | - { |
300 | - // cancel(); |
301 | - } |
302 | - |
303 | - void start() |
304 | - { |
305 | - if (!traits::Timeout<UnderlyingTimeoutType>::is_timeout_enabled(timeout)) |
306 | - { |
307 | - return; |
308 | - } |
309 | - |
310 | - // We do not keep ourselves alive to prevent from races during destruction. |
311 | - std::weak_ptr<Timeout<UnderlyingTimeoutType>> wp{this->shared_from_this()}; |
312 | - |
313 | - timer.expires_from_now( |
314 | - boost::posix_time::milliseconds( |
315 | - traits::Timeout<UnderlyingTimeoutType>::get_timeout_interval( |
316 | - timeout))); |
317 | - timer.async_wait([wp](const boost::system::error_code& ec) |
318 | - { |
319 | - auto sp = wp.lock(); |
320 | - |
321 | - if (sp) |
322 | - sp->on_timeout(ec); |
323 | - }); |
324 | - } |
325 | - |
326 | - void cancel() |
327 | - { |
328 | - try |
329 | - { |
330 | - timer.cancel(); |
331 | - } catch(...) |
332 | - { |
333 | - // Really not sure what we should do about exceptions here. |
334 | - } |
335 | - } |
336 | - |
337 | - void on_timeout(const boost::system::error_code& ec) |
338 | - { |
339 | - if (ec == boost::asio::error::operation_aborted) |
340 | - return; |
341 | - |
342 | - if (ec) |
343 | - return; |
344 | - |
345 | - traits::Timeout<UnderlyingTimeoutType>::invoke_timeout_handler(timeout); |
346 | - } |
347 | - |
348 | - boost::asio::io_service& io_service; |
349 | - boost::asio::deadline_timer timer; |
350 | - UnderlyingTimeoutType* timeout; |
351 | - }; |
352 | - |
353 | - template<typename UnderlyingWatchType = DBusWatch> |
354 | - struct Watch : std::enable_shared_from_this<Watch<UnderlyingWatchType>> |
355 | - { |
356 | - Watch(boost::asio::io_service& io_service, UnderlyingWatchType* watch) : io_service(io_service), |
357 | - stream_descriptor(io_service), |
358 | - watch(watch) |
359 | - { |
360 | - if (!watch) |
361 | - throw std::runtime_error("Precondition violated: watch has to be non-null"); |
362 | - } |
363 | - |
364 | - ~Watch() noexcept |
365 | - { |
366 | - stream_descriptor.cancel(); |
367 | - stream_descriptor.release(); |
368 | - } |
369 | - |
370 | - void start() |
371 | - { |
372 | - stream_descriptor.assign(traits::Watch<UnderlyingWatchType>::get_watch_unix_fd(watch)); |
373 | - restart(); |
374 | - } |
375 | - |
376 | - void restart() |
377 | - { |
378 | - // We do not keep ourselves alive to prevent from races during destruction. |
379 | - std::weak_ptr<Watch<UnderlyingWatchType>> wp{this->shared_from_this()}; |
380 | - |
381 | - if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_readable(watch)) |
382 | - { |
383 | - stream_descriptor.async_read_some(boost::asio::null_buffers(), [wp](boost::system::error_code ec, std::size_t bytes_transferred) |
384 | - { |
385 | - auto sp = wp.lock(); |
386 | - |
387 | - if (sp) |
388 | - sp->on_stream_descriptor_event( |
389 | - traits::Watch<UnderlyingWatchType>::readable_event(), |
390 | - ec, |
391 | - bytes_transferred); |
392 | - }); |
393 | - } |
394 | - |
395 | - if (traits::Watch<UnderlyingWatchType>::is_watch_monitoring_fd_for_writable(watch)) |
396 | - { |
397 | - stream_descriptor.async_write_some(boost::asio::null_buffers(), [wp](boost::system::error_code ec, std::size_t bytes_transferred) |
398 | - { |
399 | - auto sp = wp.lock(); |
400 | - |
401 | - if (sp) |
402 | - sp->on_stream_descriptor_event( |
403 | - traits::Watch<UnderlyingWatchType>::writeable_event(), |
404 | - ec, |
405 | - bytes_transferred); |
406 | - }); |
407 | - } |
408 | - } |
409 | - |
410 | - void cancel() |
411 | - { |
412 | - try |
413 | - { |
414 | - stream_descriptor.cancel(); |
415 | - } |
416 | - catch (...) |
417 | - { |
418 | - } |
419 | - } |
420 | - |
421 | - void on_stream_descriptor_event(int event, const boost::system::error_code& error, std::size_t) |
422 | - { |
423 | - if (error == boost::asio::error::operation_aborted) |
424 | - { |
425 | - return; |
426 | - } |
427 | - |
428 | - if (error) |
429 | - { |
430 | - traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event( |
431 | - watch, |
432 | - traits::Watch<UnderlyingWatchType>::error_event()); |
433 | - } |
434 | - else |
435 | - { |
436 | - if (!traits::Watch<UnderlyingWatchType>::invoke_watch_handler_for_event(watch, event)) |
437 | - throw std::runtime_error("Insufficient memory while handling watch event"); |
438 | - |
439 | - restart(); |
440 | - } |
441 | - } |
442 | - |
443 | - boost::asio::io_service& io_service; |
444 | - boost::asio::posix::stream_descriptor stream_descriptor; |
445 | - UnderlyingWatchType* watch; |
446 | - }; |
447 | - |
448 | - template<typename T> |
449 | - struct Holder |
450 | - { |
451 | - static void ptr_delete(void* p) |
452 | - { |
453 | - delete static_cast<Holder<T>*>(p); |
454 | - } |
455 | - |
456 | - Holder(const T& t) : value(t) |
457 | - { |
458 | - } |
459 | - |
460 | - T value; |
461 | - }; |
462 | - |
463 | - static dbus_bool_t on_dbus_add_watch(DBusWatch* watch, void* data) |
464 | - { |
465 | - if (dbus_watch_get_enabled(watch) == FALSE) |
466 | - return TRUE; |
467 | - |
468 | - auto thiz = static_cast<Executor*>(data); |
469 | - auto w = std::shared_ptr<Watch<>>(new Watch<>(thiz->io_service, watch)); |
470 | - auto holder = new Holder<std::shared_ptr<Watch<>>>(w); |
471 | - dbus_watch_set_data(watch, holder, Holder<std::shared_ptr<Watch<>>>::ptr_delete); |
472 | - |
473 | - w->start(); |
474 | - |
475 | - return TRUE; |
476 | - } |
477 | - |
478 | - static void on_dbus_remove_watch(DBusWatch* watch, void*) |
479 | - { |
480 | - auto w = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch)); |
481 | - if (!w) |
482 | - return; |
483 | - w->value->cancel(); |
484 | - } |
485 | - |
486 | - static void on_dbus_watch_toggled(DBusWatch* watch, void*) |
487 | - { |
488 | - auto holder = static_cast<Holder<std::shared_ptr<Watch<>>>*>(dbus_watch_get_data(watch)); |
489 | - if (!holder) |
490 | - return; |
491 | - dbus_watch_get_enabled(watch) == TRUE ? holder->value->restart() : holder->value->cancel(); |
492 | - } |
493 | - |
494 | - static dbus_bool_t on_dbus_add_timeout(DBusTimeout* timeout, void* data) |
495 | - { |
496 | - auto thiz = static_cast<Executor*>(data); |
497 | - auto t = std::shared_ptr<Timeout<>>(new Timeout<>(thiz->io_service, timeout)); |
498 | - auto holder = new Holder<std::shared_ptr<Timeout<>>>(t); |
499 | - dbus_timeout_set_data( |
500 | - timeout, |
501 | - holder, |
502 | - Holder<std::shared_ptr<Timeout<>>>::ptr_delete); |
503 | - |
504 | - t->start(); |
505 | - return TRUE; |
506 | - } |
507 | - |
508 | - static void on_dbus_remove_timeout(DBusTimeout* timeout, void*) |
509 | - { |
510 | - static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout))->value->cancel(); |
511 | - } |
512 | - |
513 | - static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*) |
514 | - { |
515 | - auto holder = static_cast<Holder<std::shared_ptr<Timeout<>>>*>(dbus_timeout_get_data(timeout)); |
516 | - holder->value->start(); |
517 | - } |
518 | - |
519 | - static void on_dbus_wakeup_event_loop(void* data) |
520 | - { |
521 | - auto thiz = static_cast<Executor*>(data); |
522 | - auto bus = thiz->bus; |
523 | - thiz->io_service.post([bus]() |
524 | - { |
525 | - while (dbus_connection_get_dispatch_status(bus->raw()) == DBUS_DISPATCH_DATA_REMAINS) |
526 | - { |
527 | - dbus_connection_dispatch(bus->raw()); |
528 | - } |
529 | - }); |
530 | - } |
531 | - |
532 | -public: |
533 | - |
534 | - Executor(const Bus::Ptr& bus, boost::asio::io_service& io) : bus(bus), io_service(io), work(io_service) |
535 | - { |
536 | - if (!bus) |
537 | - throw std::runtime_error("Precondition violated, cannot construct executor for null bus."); |
538 | - |
539 | - if (!dbus_connection_set_watch_functions( |
540 | - bus->raw(), |
541 | - on_dbus_add_watch, |
542 | - on_dbus_remove_watch, |
543 | - on_dbus_watch_toggled, |
544 | - this, |
545 | - nullptr)) |
546 | - throw std::runtime_error("Problem installing watch functions."); |
547 | - |
548 | - if (!dbus_connection_set_timeout_functions( |
549 | - bus->raw(), |
550 | - on_dbus_add_timeout, |
551 | - on_dbus_remove_timeout, |
552 | - on_dbus_timeout_toggled, |
553 | - this, |
554 | - nullptr)) |
555 | - throw std::runtime_error("Problem installing timeout functions."); |
556 | - |
557 | - dbus_connection_set_wakeup_main_function( |
558 | - bus->raw(), |
559 | - on_dbus_wakeup_event_loop, |
560 | - this, |
561 | - nullptr); |
562 | - } |
563 | - |
564 | - ~Executor() noexcept |
565 | - { |
566 | - stop(); |
567 | - } |
568 | - |
569 | - void run() |
570 | - { |
571 | - io_service.run(); |
572 | - } |
573 | - |
574 | - void stop() |
575 | - { |
576 | - io_service.stop(); |
577 | - } |
578 | - |
579 | -private: |
580 | - Bus::Ptr bus; |
581 | - boost::asio::io_service& io_service; |
582 | - boost::asio::io_service::work work; |
583 | -}; |
584 | - |
585 | -ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus) |
586 | -{ |
587 | - static boost::asio::io_service io; |
588 | - return std::make_shared<core::dbus::asio::Executor>(bus, io); |
589 | -} |
590 | - |
591 | -ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor::Ptr make_executor(const Bus::Ptr& bus, boost::asio::io_service& io) |
592 | -{ |
593 | - return std::make_shared<core::dbus::asio::Executor>(bus, io); |
594 | -} |
595 | - |
596 | -} |
597 | -} |
598 | -} |
599 | - |
600 | +core::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) |
601 | +{ |
602 | + return Executor::Timeout::Ptr(new Executor::Timeout(io_service, period)); |
603 | +} |
604 | + |
605 | +core::dbus::asio::Executor::Timeout::Timeout(const std::shared_ptr<boost::asio::io_service>& ios, const std::chrono::milliseconds& ms) |
606 | + : ios(ios), |
607 | + timer(*ios), |
608 | + period(ms) |
609 | +{ |
610 | +} |
611 | + |
612 | +core::dbus::asio::Executor::Timeout::~Timeout() |
613 | +{ |
614 | + // cancel(); |
615 | +} |
616 | + |
617 | +void core::dbus::asio::Executor::Timeout::start() |
618 | +{ |
619 | + // We do not keep ourselves alive to prevent from races during destruction. |
620 | + std::weak_ptr<Timeout> wp{this->shared_from_this()}; |
621 | + |
622 | + timer.expires_from_now(boost::posix_time::milliseconds(period.count())); |
623 | + timer.async_wait([wp](const boost::system::error_code& ec) |
624 | + { |
625 | + if (auto sp = wp.lock()) |
626 | + sp->on_timeout(ec); |
627 | + }); |
628 | +} |
629 | + |
630 | +void core::dbus::asio::Executor::Timeout::stop() |
631 | +{ |
632 | + try |
633 | + { |
634 | + timer.cancel(); |
635 | + } catch(...) |
636 | + { |
637 | + // Really not sure what we should do about exceptions here. |
638 | + } |
639 | +} |
640 | + |
641 | +const core::Signal<void>& core::dbus::asio::Executor::Timeout::on_triggered() |
642 | +{ |
643 | + return triggered; |
644 | +} |
645 | + |
646 | +void core::dbus::asio::Executor::Timeout::on_timeout(const boost::system::error_code& ec) |
647 | +{ |
648 | + if (not ec) |
649 | + triggered(); |
650 | +} |
651 | + |
652 | +core::dbus::asio::Executor::Watch::Ptr core::dbus::asio::Executor::Watch::create(const std::shared_ptr<boost::asio::io_service>& io_service, int fd) |
653 | +{ |
654 | + return Executor::Watch::Ptr(new Executor::Watch(io_service, fd)); |
655 | +} |
656 | + |
657 | +core::dbus::asio::Executor::Watch::Watch(const std::shared_ptr<boost::asio::io_service>& ios, int fd) |
658 | + : ios(ios), |
659 | + stream_descriptor(*ios, ::dup(fd)) |
660 | +{ |
661 | +} |
662 | + |
663 | +core::dbus::asio::Executor::Watch::~Watch() |
664 | +{ |
665 | +} |
666 | + |
667 | +void core::dbus::asio::Executor::Watch::start_with_flags(Watch::Flags flags) |
668 | +{ |
669 | + // We do not keep ourselves alive to prevent from races during destruction. |
670 | + std::weak_ptr<Watch> wp{this->shared_from_this()}; |
671 | + |
672 | + if ((flags & Flags::readable) == Flags::readable) |
673 | + { |
674 | + stream_descriptor.async_read_some(boost::asio::null_buffers(), [wp, flags](boost::system::error_code ec, std::size_t bytes_transferred) |
675 | + { |
676 | + if (auto sp = wp.lock()) |
677 | + { |
678 | + if (ec != boost::asio::error::operation_aborted) |
679 | + sp->on_stream_descriptor_event(ec ? Flags::error : Flags::readable, flags, bytes_transferred); |
680 | + } |
681 | + }); |
682 | + } |
683 | + |
684 | + if ((flags & Flags::writable) == Flags::writable) |
685 | + { |
686 | + stream_descriptor.async_write_some(boost::asio::null_buffers(), [wp, flags](boost::system::error_code ec, std::size_t bytes_transferred) |
687 | + { |
688 | + if (auto sp = wp.lock()) |
689 | + { |
690 | + if (ec != boost::asio::error::operation_aborted) |
691 | + sp->on_stream_descriptor_event(ec ? Flags::error : Flags::writable, flags, bytes_transferred); |
692 | + } |
693 | + }); |
694 | + } |
695 | +} |
696 | + |
697 | +void core::dbus::asio::Executor::Watch::stop() |
698 | +{ |
699 | + try |
700 | + { |
701 | + stream_descriptor.cancel(); |
702 | + } |
703 | + catch (...) |
704 | + { |
705 | + } |
706 | +} |
707 | + |
708 | +const core::Signal<core::dbus::Executor::Watch::Flags>& core::dbus::asio::Executor::Watch::on_triggered() |
709 | +{ |
710 | + return triggered; |
711 | +} |
712 | + |
713 | +void core::dbus::asio::Executor::Watch::on_stream_descriptor_event(Watch::Flags flags, Watch::Flags requested, std::size_t) |
714 | +{ |
715 | + const bool has_error = (flags & Flags::error) == Flags::error; |
716 | + |
717 | + triggered(flags); |
718 | + |
719 | + if (not has_error) |
720 | + start_with_flags(requested); |
721 | +} |
722 | + |
723 | +core::dbus::asio::Executor::Executor(const std::shared_ptr<boost::asio::io_service>& ios) : ios(ios), work(*ios) |
724 | +{ |
725 | +} |
726 | + |
727 | +core::dbus::asio::Executor::~Executor() |
728 | +{ |
729 | + stop(); |
730 | +} |
731 | + |
732 | +core::dbus::Executor::Watch::Ptr core::dbus::asio::Executor::add_watch(int fd) |
733 | +{ |
734 | + return asio::Executor::Watch::create(ios, fd); |
735 | +} |
736 | + |
737 | +core::dbus::Executor::Timeout::Ptr core::dbus::asio::Executor::add_timeout(const std::chrono::milliseconds& duration) |
738 | +{ |
739 | + return asio::Executor::Timeout::create(ios, duration); |
740 | +} |
741 | + |
742 | +void core::dbus::asio::Executor::dispatch(const Executor::Task& task) |
743 | +{ |
744 | + ios->post([task]() |
745 | + { |
746 | + task(); |
747 | + }); |
748 | +} |
749 | + |
750 | +void core::dbus::asio::Executor::run() |
751 | +{ |
752 | + ios->run(); |
753 | +} |
754 | + |
755 | +void core::dbus::asio::Executor::stop() |
756 | +{ |
757 | + ios->stop(); |
758 | +} |
759 | + |
760 | +core::dbus::Executor::Ptr core::dbus::asio::make_executor(const Bus::Ptr&) |
761 | +{ |
762 | + static auto ios = std::make_shared<boost::asio::io_service>(); |
763 | + return std::make_shared<core::dbus::asio::Executor>(ios); |
764 | +} |
765 | + |
766 | +core::dbus::Executor::Ptr core::dbus::asio::make_executor(const Bus::Ptr&, const std::shared_ptr<boost::asio::io_service>& ios) |
767 | +{ |
768 | + return std::make_shared<core::dbus::asio::Executor>(ios); |
769 | +} |
770 | |
771 | === added file 'src/core/dbus/asio/asio.h' |
772 | --- src/core/dbus/asio/asio.h 1970-01-01 00:00:00 +0000 |
773 | +++ src/core/dbus/asio/asio.h 2016-01-18 16:49:39 +0000 |
774 | @@ -0,0 +1,108 @@ |
775 | +/* |
776 | + * Copyright © 2016 Canonical Ltd. |
777 | + * |
778 | + * This program is free software: you can redistribute it and/or modify it |
779 | + * under the terms of the GNU Lesser General Public License version 3, |
780 | + * as published by the Free Software Foundation. |
781 | + * |
782 | + * This program is distributed in the hope that it will be useful, |
783 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
784 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
785 | + * GNU Lesser General Public License for more details. |
786 | + * |
787 | + * You should have received a copy of the GNU Lesser General Public License |
788 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
789 | + * |
790 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
791 | + */ |
792 | + |
793 | +#ifndef CORE_DBUS_ASIO_EXECUTOR_H_ |
794 | +#define CORE_DBUS_ASIO_EXECUTOR_H_ |
795 | + |
796 | +#include <core/dbus/bus.h> |
797 | +#include <core/dbus/executor.h> |
798 | +#include <core/dbus/visibility.h> |
799 | + |
800 | +#include <boost/asio.hpp> |
801 | + |
802 | +#include <dbus/dbus.h> |
803 | + |
804 | +namespace core |
805 | +{ |
806 | +namespace dbus |
807 | +{ |
808 | +namespace asio |
809 | +{ |
810 | +class ORG_FREEDESKTOP_DBUS_DLL_PUBLIC Executor : public core::dbus::Executor |
811 | +{ |
812 | +public: |
813 | + typedef std::shared_ptr<Executor> Ptr; |
814 | + |
815 | + class Timeout : public std::enable_shared_from_this<Timeout>, public core::dbus::Executor::Timeout |
816 | + { |
817 | + public: |
818 | + typedef std::shared_ptr<Timeout> Ptr; |
819 | + |
820 | + static Ptr create(const std::shared_ptr<boost::asio::io_service>& io_service, const std::chrono::milliseconds& period); |
821 | + |
822 | + ~Timeout(); |
823 | + |
824 | + void start() override; |
825 | + void stop() override; |
826 | + const core::Signal<void>& on_triggered() override; |
827 | + |
828 | + private: |
829 | + Timeout(const std::shared_ptr<boost::asio::io_service>& io_service, const std::chrono::milliseconds& period); |
830 | + void on_timeout(const boost::system::error_code& ec); |
831 | + |
832 | + std::shared_ptr<boost::asio::io_service> ios; |
833 | + core::Signal<void> triggered; |
834 | + boost::asio::deadline_timer timer; |
835 | + std::chrono::milliseconds period; |
836 | + }; |
837 | + |
838 | + class Watch : public std::enable_shared_from_this<Watch>, |
839 | + public core::dbus::Executor::Watch |
840 | + { |
841 | + public: |
842 | + typedef std::shared_ptr<Watch> Ptr; |
843 | + static Ptr create(const std::shared_ptr<boost::asio::io_service>& io_service, int fd); |
844 | + |
845 | + ~Watch() noexcept(true); |
846 | + |
847 | + void start_with_flags(Watch::Flags flags) override; |
848 | + void stop() override; |
849 | + const core::Signal<Flags>& on_triggered() override; |
850 | + |
851 | + private: |
852 | + Watch(const std::shared_ptr<boost::asio::io_service>& io_service, int fd); |
853 | + void restart(); |
854 | + void on_stream_descriptor_event(Watch::Flags flags, Watch::Flags requested, std::size_t); |
855 | + |
856 | + std::shared_ptr<boost::asio::io_service> ios; |
857 | + core::Signal<Watch::Flags> triggered; |
858 | + boost::asio::posix::stream_descriptor stream_descriptor; |
859 | + }; |
860 | + |
861 | +public: |
862 | + Executor(const std::shared_ptr<boost::asio::io_service>& ios); |
863 | + |
864 | + ~Executor() noexcept(true); |
865 | + |
866 | + core::dbus::Executor::Watch::Ptr add_watch(int fd) override; |
867 | + core::dbus::Executor::Timeout::Ptr add_timeout(const std::chrono::milliseconds& duration) override; |
868 | + void dispatch(const Task& task) override; |
869 | + |
870 | + void run() override; |
871 | + void stop() override; |
872 | + |
873 | +private: |
874 | + std::shared_ptr<boost::asio::io_service> ios; |
875 | + boost::asio::io_service::work work; |
876 | +}; |
877 | + |
878 | +} |
879 | +} |
880 | +} |
881 | + |
882 | +#endif // CORE_DBUS_ASIO_EXECUTOR_H_ |
883 | |
884 | === modified file 'src/core/dbus/bus.cpp' |
885 | --- src/core/dbus/bus.cpp 2016-01-02 22:02:10 +0000 |
886 | +++ src/core/dbus/bus.cpp 2016-01-18 16:49:39 +0000 |
887 | @@ -21,8 +21,7 @@ |
888 | #include <core/dbus/match_rule.h> |
889 | #include <core/dbus/object.h> |
890 | |
891 | -#include <core/dbus/traits/timeout.h> |
892 | -#include <core/dbus/traits/watch.h> |
893 | +#include <dbus/dbus.h> |
894 | |
895 | #include <core/posix/this_process.h> |
896 | |
897 | @@ -32,6 +31,112 @@ |
898 | |
899 | namespace |
900 | { |
901 | +namespace timeout |
902 | +{ |
903 | +bool is_enabled(DBusTimeout* timeout) |
904 | +{ |
905 | + return TRUE == dbus_timeout_get_enabled(timeout); |
906 | +} |
907 | + |
908 | +std::chrono::milliseconds get_interval(DBusTimeout* timeout) |
909 | +{ |
910 | + return std::chrono::milliseconds{dbus_timeout_get_interval(timeout)}; |
911 | +} |
912 | + |
913 | +void set_data(DBusTimeout* timeout, void* data, void (*deleter)(void*)) |
914 | +{ |
915 | + dbus_timeout_set_data(timeout, data, deleter); |
916 | +} |
917 | + |
918 | +void invoke_handler_or_throw(DBusTimeout* timeout) |
919 | +{ |
920 | + if (not dbus_timeout_handle(timeout)) |
921 | + throw std::runtime_error{"Error invoking dbus timeout handler"}; |
922 | +} |
923 | +} |
924 | + |
925 | +namespace watch |
926 | +{ |
927 | +constexpr int readable_event() |
928 | +{ |
929 | + return DBUS_WATCH_READABLE; |
930 | +} |
931 | + |
932 | +constexpr int writeable_event() |
933 | +{ |
934 | + return DBUS_WATCH_WRITABLE; |
935 | +} |
936 | + |
937 | +constexpr int error_event() |
938 | +{ |
939 | + return DBUS_WATCH_ERROR; |
940 | +} |
941 | + |
942 | +constexpr int hangup_event() |
943 | +{ |
944 | + return DBUS_WATCH_HANGUP; |
945 | +} |
946 | + |
947 | +bool is_watch_enabled(DBusWatch* watch) |
948 | +{ |
949 | + return TRUE == dbus_watch_get_enabled(watch); |
950 | +} |
951 | + |
952 | +int get_unix_fd(DBusWatch* watch) |
953 | +{ |
954 | + return dbus_watch_get_unix_fd(watch); |
955 | +} |
956 | + |
957 | +bool is_monitoring_fd_for_readable(DBusWatch* watch) |
958 | +{ |
959 | + return dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE; |
960 | +} |
961 | + |
962 | +bool is_monitoring_fd_for_writable(DBusWatch* watch) |
963 | +{ |
964 | + return dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE; |
965 | +} |
966 | + |
967 | +void* get_data(DBusWatch* watch) |
968 | +{ |
969 | + return dbus_watch_get_data(watch); |
970 | +} |
971 | + |
972 | +void set_data(DBusWatch* watch, void* data, void (*deleter)(void*)) |
973 | +{ |
974 | + dbus_watch_set_data(watch, data, deleter); |
975 | +} |
976 | + |
977 | +void invoke_handler_or_throw(DBusWatch* watch, int event) |
978 | +{ |
979 | + if (not dbus_watch_handle(watch, event)) |
980 | + { |
981 | + throw std::runtime_error{"Error invoking DBus watch handler."}; |
982 | + } |
983 | +} |
984 | +} |
985 | + |
986 | +template<typename T> |
987 | +struct Holder |
988 | +{ |
989 | + static void ptr_delete(void* p) |
990 | + { |
991 | + delete static_cast<T*>(p); |
992 | + } |
993 | + |
994 | + Holder(const T& t) : value(t) |
995 | + { |
996 | + } |
997 | + |
998 | + T value; |
999 | +}; |
1000 | + |
1001 | +template<typename T> |
1002 | +Holder<T>* make_holder(const T& value) |
1003 | +{ |
1004 | + return new Holder<T>(value); |
1005 | +} |
1006 | + |
1007 | struct VTable |
1008 | { |
1009 | static void unregister_object_path(DBusConnection*, void* data) |
1010 | @@ -127,6 +232,126 @@ |
1011 | init_libdbus_thread_support_and_install_shutdown_handler(); |
1012 | } |
1013 | |
1014 | + static dbus_bool_t on_dbus_add_watch(DBusWatch* watch, void* data) |
1015 | + { |
1016 | + auto thiz = static_cast<Private*>(data); |
1017 | + |
1018 | + if (not thiz) |
1019 | + return FALSE; |
1020 | + |
1021 | + Executor::Watch::Flags f{Executor::Watch::Flags::none}; |
1022 | + |
1023 | + if (watch::is_monitoring_fd_for_readable(watch)) |
1024 | + f = f | Executor::Watch::Flags::readable; |
1025 | + if (watch::is_monitoring_fd_for_writable(watch)) |
1026 | + f = f | Executor::Watch::Flags::writable; |
1027 | + |
1028 | + auto w = thiz->executor->add_watch(watch::get_unix_fd(watch)); |
1029 | + |
1030 | + w->on_triggered().connect([watch](Executor::Watch::Flags flags) |
1031 | + { |
1032 | + int event = 0; |
1033 | + |
1034 | + if ((flags & Executor::Watch::Flags::readable) == Executor::Watch::Flags::readable) |
1035 | + event |= watch::readable_event(); |
1036 | + if ((flags & Executor::Watch::Flags::writable) == Executor::Watch::Flags::writable) |
1037 | + event |= watch::writeable_event(); |
1038 | + if ((flags & Executor::Watch::Flags::error) == Executor::Watch::Flags::error) |
1039 | + event |= watch::error_event(); |
1040 | + if ((flags & Executor::Watch::Flags::hangup) == Executor::Watch::Flags::hangup) |
1041 | + event |= watch::hangup_event(); |
1042 | + |
1043 | + watch::invoke_handler_or_throw(watch, event); |
1044 | + }); |
1045 | + |
1046 | + watch::set_data(watch, make_holder(w), Holder<Executor::Watch::Ptr>::ptr_delete); |
1047 | + |
1048 | + if (watch::is_watch_enabled(watch)) |
1049 | + w->start_with_flags(f); |
1050 | + |
1051 | + return TRUE; |
1052 | + } |
1053 | + |
1054 | + static void on_dbus_remove_watch(DBusWatch* watch, void*) |
1055 | + { |
1056 | + if (auto w = static_cast<Holder<Executor::Watch::Ptr>*>(watch::get_data(watch))) |
1057 | + w->value->stop(); |
1058 | + } |
1059 | + |
1060 | + static void on_dbus_watch_toggled(DBusWatch* watch, void*) |
1061 | + { |
1062 | + if (auto holder = static_cast<Holder<Executor::Watch::Ptr>*>(watch::get_data(watch))) |
1063 | + { |
1064 | + Executor::Watch::Flags f{Executor::Watch::Flags::none}; |
1065 | + |
1066 | + if (watch::is_monitoring_fd_for_readable(watch)) |
1067 | + f = f | Executor::Watch::Flags::readable; |
1068 | + if (watch::is_monitoring_fd_for_writable(watch)) |
1069 | + f = f | Executor::Watch::Flags::writable; |
1070 | + |
1071 | + if (watch::is_watch_enabled(watch)) |
1072 | + holder->value->start_with_flags(f); |
1073 | + else |
1074 | + holder->value->stop(); |
1075 | + } |
1076 | + } |
1077 | + |
1078 | + static dbus_bool_t on_dbus_add_timeout(DBusTimeout* timeout, void* data) |
1079 | + { |
1080 | + auto thiz = static_cast<Private*>(data); |
1081 | + |
1082 | + if (not thiz) |
1083 | + return FALSE; |
1084 | + |
1085 | + const auto interval = timeout::get_interval(timeout); |
1086 | + |
1087 | + auto t = thiz->executor->add_timeout(interval); |
1088 | + |
1089 | + t->on_triggered().connect([timeout]() |
1090 | + { |
1091 | + timeout::invoke_handler_or_throw(timeout); |
1092 | + }); |
1093 | + |
1094 | + timeout::set_data(timeout, make_holder(t), Holder<Executor::Timeout::Ptr>::ptr_delete); |
1095 | + |
1096 | + if (timeout::is_enabled(timeout)) |
1097 | + t->start(); |
1098 | + |
1099 | + return TRUE; |
1100 | + } |
1101 | + |
1102 | + static void on_dbus_remove_timeout(DBusTimeout* timeout, void*) |
1103 | + { |
1104 | + if (auto holder = static_cast<Holder<Executor::Timeout::Ptr>*>(dbus_timeout_get_data(timeout))) |
1105 | + holder->value->stop(); |
1106 | + } |
1107 | + |
1108 | + static void on_dbus_timeout_toggled(DBusTimeout* timeout, void*) |
1109 | + { |
1110 | + if (auto holder = static_cast<Holder<Executor::Timeout::Ptr>*>(dbus_timeout_get_data(timeout))) |
1111 | + { |
1112 | + if (timeout::is_enabled(timeout)) |
1113 | + holder->value->start(); |
1114 | + else |
1115 | + holder->value->stop(); |
1116 | + } |
1117 | + |
1118 | + } |
1119 | + |
1120 | + static void on_dbus_wakeup_event_loop(void* data) |
1121 | + { |
1122 | + auto thiz = static_cast<Private*>(data); |
1123 | + auto connection = thiz->connection; |
1124 | + |
1125 | + thiz->executor->dispatch([connection]() |
1126 | + { |
1127 | + while (dbus_connection_get_dispatch_status(connection.get()) == DBUS_DISPATCH_DATA_REMAINS) |
1128 | + { |
1129 | + dbus_connection_dispatch(connection.get()); |
1130 | + } |
1131 | + }); |
1132 | + } |
1133 | + |
1134 | std::shared_ptr<DBusConnection> connection; |
1135 | std::shared_ptr<MessageFactory> message_factory_impl; |
1136 | Executor::Ptr executor; |
1137 | @@ -339,6 +564,30 @@ |
1138 | void Bus::install_executor(const Executor::Ptr& e) |
1139 | { |
1140 | d->executor = e; |
1141 | + |
1142 | + if (!dbus_connection_set_watch_functions( |
1143 | + d->connection.get(), |
1144 | + Private::on_dbus_add_watch, |
1145 | + Private::on_dbus_remove_watch, |
1146 | + Private::on_dbus_watch_toggled, |
1147 | + d.get(), |
1148 | + nullptr)) |
1149 | + throw std::runtime_error("Problem installing watch functions."); |
1150 | + |
1151 | + if (!dbus_connection_set_timeout_functions( |
1152 | + d->connection.get(), |
1153 | + Private::on_dbus_add_timeout, |
1154 | + Private::on_dbus_remove_timeout, |
1155 | + Private::on_dbus_timeout_toggled, |
1156 | + d.get(), |
1157 | + nullptr)) |
1158 | + throw std::runtime_error("Problem installing timeout functions."); |
1159 | + |
1160 | + dbus_connection_set_wakeup_main_function( |
1161 | + d->connection.get(), |
1162 | + Private::on_dbus_wakeup_event_loop, |
1163 | + d.get(), |
1164 | + nullptr); |
1165 | } |
1166 | |
1167 | void Bus::stop() |
1168 | |
1169 | === added file 'src/core/dbus/executor.cpp' |
1170 | --- src/core/dbus/executor.cpp 1970-01-01 00:00:00 +0000 |
1171 | +++ src/core/dbus/executor.cpp 2016-01-18 16:49:39 +0000 |
1172 | @@ -0,0 +1,42 @@ |
1173 | +/* |
1174 | + * Copyright © 2012 Canonical Ltd. |
1175 | + * |
1176 | + * This program is free software: you can redistribute it and/or modify it |
1177 | + * under the terms of the GNU Lesser General Public License version 3, |
1178 | + * as published by the Free Software Foundation. |
1179 | + * |
1180 | + * This program is distributed in the hope that it will be useful, |
1181 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1182 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1183 | + * GNU Lesser General Public License for more details. |
1184 | + * |
1185 | + * You should have received a copy of the GNU Lesser General Public License |
1186 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1187 | + * |
1188 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1189 | + */ |
1190 | + |
1191 | +#include <core/dbus/executor.h> |
1192 | + |
1193 | +#include <bitset> |
1194 | +#include <iostream> |
1195 | + |
1196 | +namespace |
1197 | +{ |
1198 | +using NT = typename std::underlying_type<core::dbus::Executor::Watch::Flags>::type; |
1199 | +} |
1200 | + |
1201 | +std::ostream& core::dbus::operator<<(std::ostream& out, core::dbus::Executor::Watch::Flags flags) |
1202 | +{ |
1203 | + return out << "Watch::Flags[" << std::bitset<4*sizeof(NT)>(static_cast<NT>(flags)).to_string() << "]"; |
1204 | +} |
1205 | + |
1206 | +core::dbus::Executor::Watch::Flags core::dbus::operator|(core::dbus::Executor::Watch::Flags lhs, core::dbus::Executor::Watch::Flags rhs) |
1207 | +{ |
1208 | + return static_cast<core::dbus::Executor::Watch::Flags>(static_cast<NT>(lhs) | static_cast<NT>(rhs)); |
1209 | +} |
1210 | + |
1211 | +core::dbus::Executor::Watch::Flags core::dbus::operator&(core::dbus::Executor::Watch::Flags lhs, core::dbus::Executor::Watch::Flags rhs) |
1212 | +{ |
1213 | + return static_cast<core::dbus::Executor::Watch::Flags>(static_cast<NT>(lhs) & static_cast<NT>(rhs)); |
1214 | +} |
1215 | |
1216 | === modified file 'tests/CMakeLists.txt' |
1217 | --- tests/CMakeLists.txt 2014-09-28 21:27:09 +0000 |
1218 | +++ tests/CMakeLists.txt 2016-01-18 16:49:39 +0000 |
1219 | @@ -31,6 +31,7 @@ |
1220 | ${CMAKE_CURRENT_BINARY_DIR}/test_data.h @ONLY) |
1221 | |
1222 | include_directories( |
1223 | + ${CMAKE_SOURCE_DIR}/src |
1224 | ${CMAKE_CURRENT_BINARY_DIR} |
1225 | ) |
1226 | |
1227 | |
1228 | === modified file 'tests/executor_test.cpp' |
1229 | --- tests/executor_test.cpp 2014-09-28 21:27:09 +0000 |
1230 | +++ tests/executor_test.cpp 2016-01-18 16:49:39 +0000 |
1231 | @@ -16,7 +16,8 @@ |
1232 | * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1233 | */ |
1234 | |
1235 | -#include <core/dbus/asio/executor.h> |
1236 | +#include <core/dbus/asio/make_executor.h> |
1237 | +#include <core/dbus/asio/asio.h> |
1238 | |
1239 | #include <core/dbus/dbus.h> |
1240 | #include <core/dbus/fixture.h> |
1241 | @@ -53,10 +54,15 @@ |
1242 | std::binomial_distribution<> coin{1, 1.-probability_for_failure}; |
1243 | }; |
1244 | |
1245 | +struct ExecutorWatchFlags : public ::testing::TestWithParam<core::dbus::Executor::Watch::Flags> |
1246 | +{ |
1247 | + |
1248 | +}; |
1249 | + |
1250 | struct Executor : public core::dbus::testing::Fixture |
1251 | { |
1252 | protected: |
1253 | - boost::asio::io_service io_service; |
1254 | + std::shared_ptr<boost::asio::io_service> io_service = std::make_shared<boost::asio::io_service>(); |
1255 | }; |
1256 | |
1257 | auto session_bus_config_file = |
1258 | @@ -68,16 +74,119 @@ |
1259 | core::testing::system_bus_configuration_file(); |
1260 | } |
1261 | |
1262 | -TEST_F(Executor, ThrowsOnConstructionFromNullBus) |
1263 | -{ |
1264 | - EXPECT_ANY_THROW(core::dbus::asio::make_executor(core::dbus::Bus::Ptr{}, io_service)); |
1265 | -} |
1266 | - |
1267 | -TEST_F(Executor, DoesNotThrowForExistingBus) |
1268 | -{ |
1269 | - auto bus = session_bus(); |
1270 | - EXPECT_NO_THROW(bus->install_executor(core::dbus::asio::make_executor(bus, io_service))); |
1271 | -} |
1272 | +TEST_P(ExecutorWatchFlags, BinaryOrAndWorksAsExpected) |
1273 | +{ |
1274 | + core::dbus::Executor::Watch::Flags f{core::dbus::Executor::Watch::Flags::none}; |
1275 | + EXPECT_EQ(GetParam(), (f | GetParam()) & GetParam()); |
1276 | +} |
1277 | + |
1278 | +INSTANTIATE_TEST_CASE_P(ExecutorWatchFlags, |
1279 | + ExecutorWatchFlags, |
1280 | + ::testing::Values(core::dbus::Executor::Watch::Flags::readable, |
1281 | + core::dbus::Executor::Watch::Flags::writable, |
1282 | + core::dbus::Executor::Watch::Flags::error, |
1283 | + core::dbus::Executor::Watch::Flags::hangup)); |
1284 | + |
1285 | +TEST(ExecutorWatch, TriggersForFdReadable) |
1286 | +{ |
1287 | + static constexpr const std::size_t read = 0; |
1288 | + static constexpr const std::size_t write = 1; |
1289 | + |
1290 | + int fds[2]; |
1291 | + ::pipe(fds); |
1292 | + |
1293 | + auto ios = std::make_shared<boost::asio::io_service>(); |
1294 | + boost::asio::io_service::work work{*ios}; |
1295 | + |
1296 | + auto watch = core::dbus::asio::Executor::Watch::create(ios, fds[read]); |
1297 | + watch->on_triggered().connect([ios](core::dbus::asio::Executor::Watch::Flags flags) |
1298 | + { |
1299 | + EXPECT_EQ(core::dbus::asio::Executor::Watch::Flags::readable, flags & core::dbus::asio::Executor::Watch::Flags::readable); |
1300 | + ios->stop(); |
1301 | + }); |
1302 | + watch->start_with_flags(core::dbus::asio::Executor::Watch::Flags::readable); |
1303 | + |
1304 | + ::write(fds[write], &read, sizeof(read)); |
1305 | + |
1306 | + ios->run(); |
1307 | + |
1308 | + ::close(fds[read]); ::close(fds[write]); |
1309 | +} |
1310 | + |
1311 | +TEST(ExecutorWatch, TriggersForFdWritable) |
1312 | +{ |
1313 | + static constexpr const std::size_t read = 0; |
1314 | + static constexpr const std::size_t write = 1; |
1315 | + |
1316 | + int fds[2]; |
1317 | + ::pipe(fds); |
1318 | + |
1319 | + auto ios = std::make_shared<boost::asio::io_service>(); |
1320 | + boost::asio::io_service::work work{*ios}; |
1321 | + |
1322 | + auto ww = core::dbus::asio::Executor::Watch::create(ios, fds[write]); |
1323 | + ww->on_triggered().connect([ios](core::dbus::asio::Executor::Watch::Flags flags) |
1324 | + { |
1325 | + EXPECT_EQ(core::dbus::asio::Executor::Watch::Flags::writable, flags & core::dbus::asio::Executor::Watch::Flags::writable); |
1326 | + ios->stop(); |
1327 | + }); |
1328 | + ww->start_with_flags(core::dbus::asio::Executor::Watch::Flags::writable); |
1329 | + |
1330 | + std::size_t v; |
1331 | + ::write(fds[write], &v, sizeof(v)); |
1332 | + |
1333 | + ios->run(); |
1334 | + |
1335 | + ::close(fds[read]); ::close(fds[write]); |
1336 | +} |
1337 | + |
1338 | +TEST(ExecutorTimeout, TimeoutTriggersCorrectly) |
1339 | +{ |
1340 | + auto ios = std::make_shared<boost::asio::io_service>(); |
1341 | + boost::asio::io_service::work work{*ios}; |
1342 | + |
1343 | + auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500}); |
1344 | + to->on_triggered().connect([ios]() |
1345 | + { |
1346 | + ios->stop(); |
1347 | + }); |
1348 | + |
1349 | + to->start(); |
1350 | + ios->run(); |
1351 | +} |
1352 | + |
1353 | +TEST(ExecutorTimeout, TimeoutTriggersCorrectlyForZeroDuration) |
1354 | +{ |
1355 | + auto ios = std::make_shared<boost::asio::io_service>(); |
1356 | + boost::asio::io_service::work work{*ios}; |
1357 | + |
1358 | + auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{0}); |
1359 | + to->on_triggered().connect([ios]() |
1360 | + { |
1361 | + ios->stop(); |
1362 | + }); |
1363 | + |
1364 | + to->start(); |
1365 | + ios->run(); |
1366 | +} |
1367 | + |
1368 | +TEST(ExecutorTimeout, TimeoutGoingOutOfScopeDoesNotSegfault) |
1369 | +{ |
1370 | + auto ios = std::make_shared<boost::asio::io_service>(); |
1371 | + boost::asio::io_service::work work{*ios}; |
1372 | + |
1373 | + { |
1374 | + auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500}); |
1375 | + to->start(); |
1376 | + } |
1377 | + |
1378 | + auto to = core::dbus::asio::Executor::Timeout::create(ios, std::chrono::milliseconds{500}); |
1379 | + to->on_triggered().connect([ios]() { ios->stop(); }); |
1380 | + to->start(); |
1381 | + |
1382 | + ios->run(); |
1383 | +} |
1384 | + |
1385 | |
1386 | TEST_F(Executor, ABusRunByAnExecutorReceivesSignals) |
1387 | { |
1388 | @@ -206,45 +315,30 @@ |
1389 | auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<test::Service>::interface_name()); |
1390 | auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service")); |
1391 | |
1392 | - std::thread updater1([bus, stub]() |
1393 | - { |
1394 | - for (unsigned int counter = 0; counter < 1000; counter++) |
1395 | - { |
1396 | - try |
1397 | - { |
1398 | - auto result = stub->transact_method<test::Service::Method, int64_t>(); |
1399 | - |
1400 | - if (result.is_error()) |
1401 | - std::cout << result.error().print() << std::endl; |
1402 | - else |
1403 | - EXPECT_EQ(42, result.value()); |
1404 | - |
1405 | - } catch(const std::runtime_error& e) |
1406 | - { |
1407 | - std::cout << e.what() << std::endl; |
1408 | - } |
1409 | - } |
1410 | - }); |
1411 | - |
1412 | - std::thread updater2([bus, stub]() |
1413 | - { |
1414 | - for (unsigned int counter = 0; counter < 1000; counter++) |
1415 | - { |
1416 | - try |
1417 | - { |
1418 | - auto result = stub->transact_method<test::Service::Method, int64_t>(); |
1419 | - |
1420 | - if (result.is_error()) |
1421 | - std::cout << result.error().print() << std::endl; |
1422 | - else |
1423 | - EXPECT_EQ(42, result.value()); |
1424 | - |
1425 | - } catch(const std::runtime_error& e) |
1426 | - { |
1427 | - std::cout << e.what() << std::endl; |
1428 | - } |
1429 | - } |
1430 | - }); |
1431 | + auto updater = [](const core::dbus::Object::Ptr& stub) |
1432 | + { |
1433 | + for (unsigned int counter = 0; counter < 1000; counter++) |
1434 | + { |
1435 | + try |
1436 | + { |
1437 | + auto result = stub->transact_method<test::Service::Method, int64_t>(); |
1438 | + |
1439 | + if (not result.is_error()) |
1440 | + { |
1441 | + EXPECT_EQ(42, result.value()); |
1442 | + } |
1443 | + |
1444 | + } catch(...) |
1445 | + { |
1446 | + // Dropping the exception on purpose. We have a chaos monkey running, |
1447 | + // |
1448 | + } |
1449 | + } |
1450 | + }; |
1451 | + |
1452 | + |
1453 | + std::thread updater1(updater, stub); |
1454 | + std::thread updater2(updater, stub); |
1455 | |
1456 | if (updater1.joinable()) |
1457 | updater1.join(); |
1458 | |
1459 | === modified file 'tests/service_test.cpp' |
1460 | --- tests/service_test.cpp 2014-07-30 16:12:16 +0000 |
1461 | +++ tests/service_test.cpp 2016-01-18 16:49:39 +0000 |
1462 | @@ -66,11 +66,13 @@ |
1463 | |
1464 | TEST_F(Service, AddingServiceAndObjectAndCallingIntoItSucceeds) |
1465 | { |
1466 | - core::testing::CrossProcessSync cps1; |
1467 | - |
1468 | - const int64_t expected_value = 42; |
1469 | - |
1470 | - auto service = [this, expected_value, &cps1]() |
1471 | + core::testing::CrossProcessSync cps1; |
1472 | + |
1473 | + const int64_t expected_value = 42; |
1474 | + |
1475 | + auto service = [this, expected_value, &cps1]() |
1476 | + { |
1477 | + try |
1478 | { |
1479 | core::testing::SigTermCatcher sc; |
1480 | |
1481 | @@ -87,6 +89,7 @@ |
1482 | |
1483 | skeleton->install_method_handler<test::Service::Method>([bus, skeleton, &readonly_property, expected_value](const dbus::Message::Ptr& msg) |
1484 | { |
1485 | + std::cout << "here" << std::endl; |
1486 | auto reply = dbus::Message::make_method_return(msg); |
1487 | reply->writer() << expected_value; |
1488 | bus->send(reply); |
1489 | @@ -95,9 +98,9 @@ |
1490 | auto changed_signal = skeleton->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>(); |
1491 | core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType |
1492 | args("this.is.unlikely.to.exist.Service", |
1493 | - {{test::Service::Properties::ReadOnly::name(), |
1494 | - core::dbus::types::TypedVariant<test::Service::Properties::ReadOnly::ValueType>(expected_value)}}, |
1495 | - {}); |
1496 | + {{test::Service::Properties::ReadOnly::name(), |
1497 | + core::dbus::types::TypedVariant<test::Service::Properties::ReadOnly::ValueType>(expected_value)}}, |
1498 | + {}); |
1499 | skeleton->emit_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged, core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType>(args); |
1500 | changed_signal->emit(args); |
1501 | |
1502 | @@ -115,15 +118,22 @@ |
1503 | t.join(); |
1504 | |
1505 | return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
1506 | - }; |
1507 | - |
1508 | - auto client = [this, expected_value, &cps1]() |
1509 | + } catch(const std::exception& e) |
1510 | + { |
1511 | + std::cout << e.what() << std::endl; |
1512 | + } |
1513 | + |
1514 | + return core::posix::exit::Status::failure; |
1515 | + }; |
1516 | + |
1517 | + auto client = [this, expected_value, &cps1]() |
1518 | + { |
1519 | + try |
1520 | { |
1521 | auto bus = session_bus(); |
1522 | bus->install_executor(core::dbus::asio::make_executor(bus)); |
1523 | std::thread t{[bus](){ bus->run(); }}; |
1524 | EXPECT_EQ(std::uint32_t(1), cps1.wait_for_signal_ready_for(std::chrono::milliseconds{500})); |
1525 | - |
1526 | auto stub_service = dbus::Service::use_service<test::Service>(bus); |
1527 | auto stub = stub_service->object_for_path(dbus::types::ObjectPath("/this/is/unlikely/to/exist/Service")); |
1528 | EXPECT_EQ(stub->path().as_string(), "/this/is/unlikely/to/exist/Service"); |
1529 | @@ -151,6 +161,7 @@ |
1530 | try |
1531 | { |
1532 | auto result = stub->invoke_method_synchronously<test::Service::Method, int64_t>(); |
1533 | + std::cout << "here" << std::endl; |
1534 | EXPECT_FALSE(result.is_error()); |
1535 | EXPECT_EQ(expected_value, result.value()); |
1536 | } catch(const std::exception& e) |
1537 | @@ -170,9 +181,15 @@ |
1538 | EXPECT_EQ(changed_value, expected_value); |
1539 | |
1540 | return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
1541 | - }; |
1542 | + } |
1543 | + catch (const std::exception& e) |
1544 | + { |
1545 | + std::cout << e.what() << std::endl; |
1546 | + } |
1547 | + return core::posix::exit::Status::failure; |
1548 | + }; |
1549 | |
1550 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client)); |
1551 | + EXPECT_EQ(core::testing::ForkAndRunResult::empty, core::testing::fork_and_run(service, client)); |
1552 | } |
1553 | |
1554 | TEST_F(Service, AddingANonExistingServiceDoesNotThrow) |