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