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
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)

Subscribers

People subscribed via source and target branches