Merge lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions into lp:dbus-cpp/15.04

Proposed by Thomas Voß
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: 109
Merged at revision: 102
Proposed branch: lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions
Merge into: lp:dbus-cpp/15.04
Diff against target: 363 lines (+137/-44)
6 files modified
include/core/dbus/impl/object.h (+95/-34)
include/core/dbus/impl/property.h (+21/-0)
include/core/dbus/impl/signal.h (+4/-2)
include/core/dbus/object.h (+8/-7)
include/core/dbus/property.h (+8/-0)
src/core/dbus/service.cpp (+1/-1)
To merge this branch: bzr merge lp:~thomas-voss/dbus-cpp/fix-signal-subscriptions
Reviewer Review Type Date Requested Status
Alberto Aguirre (community) Approve
Review via email: mp+277101@code.launchpad.net

Commit message

Ensure that Signal with non-void argument types correctly narrow their match rules.

Description of the change

Ensure that Signal with non-void argument types correctly narrow their match rules.

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

Fix disconnect behavior for stub signals and lifetime issues for Signal<> instances.

104. By Thomas Voß

Handle o.f.dbus.Properties.PropertiesChanged manually to fix circular lifetime dependency.

105. By Thomas Voß

Cache properties on stub object instances.

106. By Thomas Voß

Ensure that Property::about_to_be_destroyed() is triggered in dtor.

107. By Thomas Voß

Ensure that match rules are correctly removed on property destruction.

108. By Thomas Voß

Only subscribe to PropertiesChanged once per dbus::Object instance (on demand).

109. By Thomas Voß

Remove match for PropertiesChanged in Object::~Object.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'include/core/dbus/impl/object.h'
--- include/core/dbus/impl/object.h 2014-10-30 13:22:52 +0000
+++ include/core/dbus/impl/object.h 2015-11-16 15:33:02 +0000
@@ -19,7 +19,6 @@
19#define CORE_DBUS_IMPL_OBJECT_H_19#define CORE_DBUS_IMPL_OBJECT_H_
2020
21#include <core/dbus/bus.h>21#include <core/dbus/bus.h>
22#include <core/dbus/lifetime_constrained_cache.h>
23#include <core/dbus/match_rule.h>22#include <core/dbus/match_rule.h>
24#include <core/dbus/message_router.h>23#include <core/dbus/message_router.h>
25#include <core/dbus/message_streaming_operators.h>24#include <core/dbus/message_streaming_operators.h>
@@ -37,6 +36,7 @@
3736
38#include <functional>37#include <functional>
39#include <future>38#include <future>
39#include <iostream>
40#include <map>40#include <map>
41#include <memory>41#include <memory>
42#include <string>42#include <string>
@@ -70,17 +70,17 @@
70 object_path.as_string(),70 object_path.as_string(),
71 traits::Service<typename Method::Interface>::interface_name().c_str(),71 traits::Service<typename Method::Interface>::interface_name().c_str(),
72 Method::name());72 Method::name());
73 73
74 if (!msg)74 if (!msg)
75 throw std::runtime_error("No memory available to allocate DBus message");75 throw std::runtime_error("No memory available to allocate DBus message");
76 76
77 auto writer = msg->writer();77 auto writer = msg->writer();
78 encode_message(writer, args...);78 encode_message(writer, args...);
79 79
80 auto reply = parent->get_connection()->send_with_reply_and_block_for_at_most(80 auto reply = parent->get_connection()->send_with_reply_and_block_for_at_most(
81 msg,81 msg,
82 Method::default_timeout());82 Method::default_timeout());
83 83
84 return Result<ResultType>::from_message(reply);84 return Result<ResultType>::from_message(reply);
85}85}
8686
@@ -153,40 +153,57 @@
153inline std::shared_ptr<Property<PropertyDescription>>153inline std::shared_ptr<Property<PropertyDescription>>
154Object::get_property()154Object::get_property()
155{155{
156 // If this is a proxy object we set up listening for property changes the
157 // first time someone accesses properties.
158 if (parent->is_stub())
159 {
160 if (!signal_properties_changed)
161 {
162 signal_properties_changed
163 = get_signal<interfaces::Properties::Signals::PropertiesChanged>();
164
165 signal_properties_changed->connect(
166 std::bind(
167 &Object::on_properties_changed,
168 shared_from_this(),
169 std::placeholders::_1));
170 }
171 }
172
173 typedef Property<PropertyDescription> PropertyType;156 typedef Property<PropertyDescription> PropertyType;
174 auto property =
175 PropertyType::make_property(
176 shared_from_this());
177157
158 // Creating a stub property for a remote object/property instance
159 // requires the following steps:
160 // [1.] Look up if we already have a property instance available in the cache,
161 // leveraging the tuple (path, interface, name) as key.
162 // [1.1] If yes: return the property.
163 // [1.2] If no: Create a new proeprty instance and:
164 // [1.2.1] Make it known to the cache.
165 // [1.2.2] Wire it up for property_changed signal receiving.
166 // [1.2.3] Communicate a new match rule to the dbus daemon to enable reception.
178 if (parent->is_stub())167 if (parent->is_stub())
179 {168 {
180 auto tuple = std::make_tuple(169 auto itf = traits::Service<typename PropertyDescription::Interface>::interface_name();
181 traits::Service<typename PropertyDescription::Interface>::interface_name(),170 auto name = PropertyDescription::name();
182 PropertyDescription::name());171 auto ekey = std::make_tuple(path(), itf, name);
183172
184 property_changed_vtable[tuple] = std::bind(173 auto property = Object::property_cache<PropertyDescription>().retrieve_value_for_key(ekey);
185 &Property<PropertyDescription>::handle_changed,174 if (property)
186 property,175 {
187 std::placeholders::_1);176 return property;
177 }
178
179 auto mr = MatchRule()
180 .type(Message::Type::signal)
181 .interface(traits::Service<interfaces::Properties>::interface_name())
182 .member(interfaces::Properties::Signals::PropertiesChanged::name());
183
184 property = PropertyType::make_property(shared_from_this());
185
186 Object::property_cache<PropertyDescription>().insert_value_for_key(ekey, property);
187
188 // We only ever do this once per object.
189 std::call_once(add_match_once, [&]()
190 {
191 // [1.2.4] Inform the dbus daemon that we would like to receive the respective signals.
192 add_match(mr);
193 });
194
195 // [1.2.2] Enable dispatching of changes.
196 std::weak_ptr<PropertyType> wp{property};
197 property_changed_vtable[std::make_tuple(itf, name)] = [wp](const types::Variant& arg)
198 {
199 if (auto sp = wp.lock())
200 sp->handle_changed(arg);
201 };
202
203 return property;
188 }204 }
189 return property;205
206 return PropertyType::make_property(shared_from_this());
190}207}
191208
192template<typename Interface>209template<typename Interface>
@@ -312,6 +329,23 @@
312 &MessageRouter<PropertyKey>::operator(), 329 &MessageRouter<PropertyKey>::operator(),
313 std::ref(set_property_router), 330 std::ref(set_property_router),
314 std::placeholders::_1));331 std::placeholders::_1));
332 } else
333 {
334 // We centrally route org.freedesktop.DBus.Properties.PropertiesChanged
335 // through the object, which in turn routes via a custom Property cache.
336 signal_router.install_route(
337 SignalKey{
338 traits::Service<interfaces::Properties>::interface_name(),
339 interfaces::Properties::Signals::PropertiesChanged::name()
340 },
341 // Passing 'this' is fine as the lifetime of the signal_router is upper limited
342 // by the lifetime of 'this'.
343 [this](const Message::Ptr& msg)
344 {
345 interfaces::Properties::Signals::PropertiesChanged::ArgumentType arg;
346 msg->reader() >> arg;
347 on_properties_changed(arg);
348 });
315 }349 }
316}350}
317351
@@ -319,6 +353,20 @@
319{353{
320 parent->get_connection()->access_signal_router().uninstall_route(object_path);354 parent->get_connection()->access_signal_router().uninstall_route(object_path);
321 parent->get_connection()->unregister_object_path(object_path);355 parent->get_connection()->unregister_object_path(object_path);
356
357 auto mr = MatchRule()
358 .type(Message::Type::signal)
359 .interface(traits::Service<interfaces::Properties>::interface_name())
360 .member(interfaces::Properties::Signals::PropertiesChanged::name());
361
362 try
363 {
364 remove_match(mr);
365 } catch(...)
366 {
367 // We consciously drop all possible exceptions here. There is hardly
368 // anything we can do about the error anyway.
369 }
322}370}
323371
324inline void Object::add_match(const MatchRule& rule)372inline void Object::add_match(const MatchRule& rule)
@@ -346,6 +394,19 @@
346 }394 }
347 }395 }
348}396}
397
398template<typename PropertyDescription>
399inline core::dbus::ThreadSafeLifetimeConstrainedCache<
400 core::dbus::Object::CacheKey,
401 core::dbus::Property<PropertyDescription>>&
402core::dbus::Object::property_cache()
403{
404 static core::dbus::ThreadSafeLifetimeConstrainedCache<
405 core::dbus::Object::CacheKey,
406 core::dbus::Property<PropertyDescription>
407 > cache;
408 return cache;
409}
349}410}
350}411}
351412
352413
=== modified file 'include/core/dbus/impl/property.h'
--- include/core/dbus/impl/property.h 2014-06-10 08:44:17 +0000
+++ include/core/dbus/impl/property.h 2015-11-16 15:33:02 +0000
@@ -64,6 +64,13 @@
64}64}
6565
66template<typename PropertyType>66template<typename PropertyType>
67const core::Signal<void>&
68Property<PropertyType>::about_to_be_destroyed() const
69{
70 return signal_about_to_be_destroyed;
71}
72
73template<typename PropertyType>
67std::shared_ptr<Property<PropertyType>>74std::shared_ptr<Property<PropertyType>>
68Property<PropertyType>::make_property(const std::shared_ptr<Object>& parent)75Property<PropertyType>::make_property(const std::shared_ptr<Object>& parent)
69{76{
@@ -109,6 +116,20 @@
109}116}
110117
111template<typename PropertyType>118template<typename PropertyType>
119Property<PropertyType>::~Property()
120{
121 try
122 {
123 signal_about_to_be_destroyed();
124 } catch(...)
125 {
126 // Consciously dropping all exceptions here.
127 // There is hardly anything we can do about it while
128 // tearing down the object anyway.
129 }
130}
131
132template<typename PropertyType>
112void133void
113Property<PropertyType>::handle_get(const Message::Ptr& msg)134Property<PropertyType>::handle_get(const Message::Ptr& msg)
114{135{
115136
=== modified file 'include/core/dbus/impl/signal.h'
--- include/core/dbus/impl/signal.h 2015-10-21 20:04:04 +0000
+++ include/core/dbus/impl/signal.h 2015-11-16 15:33:02 +0000
@@ -121,8 +121,8 @@
121 &Signal<SignalDescription>::operator(),121 &Signal<SignalDescription>::operator(),
122 this,122 this,
123 std::placeholders::_1));123 std::placeholders::_1));
124 parent->add_match(124 rule = rule.type(Message::Type::signal).interface(interface).member(name);
125 rule.type(Message::Type::signal).interface(interface).member(name));125 parent->add_match(rule);
126}126}
127127
128template<typename SignalDescription, typename Argument>128template<typename SignalDescription, typename Argument>
@@ -309,6 +309,8 @@
309 &Signal<SignalDescription, typename SignalDescription::ArgumentType>::operator(),309 &Signal<SignalDescription, typename SignalDescription::ArgumentType>::operator(),
310 this,310 this,
311 std::placeholders::_1));311 std::placeholders::_1));
312
313 d->rule = d->rule.type(Message::Type::signal).interface(interface).member(name);
312}314}
313315
314template<typename SignalDescription>316template<typename SignalDescription>
315317
=== modified file 'include/core/dbus/object.h'
--- include/core/dbus/object.h 2014-10-23 21:09:56 +0000
+++ include/core/dbus/object.h 2015-11-16 15:33:02 +0000
@@ -19,12 +19,14 @@
19#define CORE_DBUS_OBJECT_H_19#define CORE_DBUS_OBJECT_H_
2020
21#include <core/dbus/bus.h>21#include <core/dbus/bus.h>
22#include <core/dbus/lifetime_constrained_cache.h>
22#include <core/dbus/service.h>23#include <core/dbus/service.h>
2324
24#include <functional>25#include <functional>
25#include <future>26#include <future>
26#include <map>27#include <map>
27#include <memory>28#include <memory>
29#include <mutex>
28#include <ostream>30#include <ostream>
29#include <string>31#include <string>
3032
@@ -82,10 +84,14 @@
82class Object : public std::enable_shared_from_this<Object>84class Object : public std::enable_shared_from_this<Object>
83{85{
84 private:86 private:
87 typedef std::tuple<types::ObjectPath, std::string, std::string> CacheKey;
85 typedef std::tuple<std::string, std::string> MethodKey;88 typedef std::tuple<std::string, std::string> MethodKey;
86 typedef std::tuple<std::string, std::string> PropertyKey;89 typedef std::tuple<std::string, std::string> PropertyKey;
87 typedef std::tuple<std::string, std::string> SignalKey;90 typedef std::tuple<std::string, std::string> SignalKey;
8891
92 template<typename PropertyDescription>
93 static ThreadSafeLifetimeConstrainedCache<CacheKey, Property<PropertyDescription>>& property_cache();
94
89 public:95 public:
90 typedef std::shared_ptr<Object> Ptr;96 typedef std::shared_ptr<Object> Ptr;
91 typedef std::function<void(const Message::Ptr&)> MethodHandler;97 typedef std::function<void(const Message::Ptr&)> MethodHandler;
@@ -173,7 +179,7 @@
173 * @param [in] path The path to associate the object with.179 * @param [in] path The path to associate the object with.
174 * @return An object instance or nullptr in case of errors.180 * @return An object instance or nullptr in case of errors.
175 */181 */
176 std::shared_ptr<Object> 182 std::shared_ptr<Object>
177 inline add_object_for_path(const types::ObjectPath& path);183 inline add_object_for_path(const types::ObjectPath& path);
178184
179 /**185 /**
@@ -227,16 +233,11 @@
227 MessageRouter<MethodKey> method_router;233 MessageRouter<MethodKey> method_router;
228 MessageRouter<PropertyKey> get_property_router;234 MessageRouter<PropertyKey> get_property_router;
229 MessageRouter<PropertyKey> set_property_router;235 MessageRouter<PropertyKey> set_property_router;
236 std::once_flag add_match_once;
230 std::map<237 std::map<
231 std::tuple<std::string, std::string>,238 std::tuple<std::string, std::string>,
232 std::function<void(const types::Variant&)>239 std::function<void(const types::Variant&)>
233 > property_changed_vtable;240 > property_changed_vtable;
234 std::shared_ptr<
235 Signal<
236 interfaces::Properties::Signals::PropertiesChanged,
237 interfaces::Properties::Signals::PropertiesChanged::ArgumentType
238 >
239 > signal_properties_changed;
240};241};
241}242}
242}243}
243244
=== modified file 'include/core/dbus/property.h'
--- include/core/dbus/property.h 2014-01-20 21:22:02 +0000
+++ include/core/dbus/property.h 2015-11-16 15:33:02 +0000
@@ -43,6 +43,8 @@
43 typedef typename PropertyType::ValueType ValueType;43 typedef typename PropertyType::ValueType ValueType;
44 typedef core::Property<ValueType> Super;44 typedef core::Property<ValueType> Super;
4545
46 inline ~Property();
47
46 /**48 /**
47 * @brief Non-mutable access to the contained value.49 * @brief Non-mutable access to the contained value.
48 * @return Non-mutable reference to the contained value.50 * @return Non-mutable reference to the contained value.
@@ -61,6 +63,11 @@
61 */63 */
62 inline bool is_writable() const;64 inline bool is_writable() const;
6365
66 /**
67 * @brief Emitted during destruction of an object instance.
68 */
69 inline const core::Signal<void>& about_to_be_destroyed() const;
70
64protected:71protected:
65 friend class Object;72 friend class Object;
6673
@@ -82,6 +89,7 @@
82 std::string interface;89 std::string interface;
83 std::string name;90 std::string name;
84 bool writable;91 bool writable;
92 core::Signal<void> signal_about_to_be_destroyed;
85};93};
86}94}
87}95}
8896
=== modified file 'src/core/dbus/service.cpp'
--- src/core/dbus/service.cpp 2014-06-30 04:57:08 +0000
+++ src/core/dbus/service.cpp 2015-11-16 15:33:02 +0000
@@ -79,7 +79,7 @@
7979
80void Service::remove_match(const MatchRule& rule)80void Service::remove_match(const MatchRule& rule)
81{81{
82 connection->remove_match(rule);82 connection->remove_match(rule.sender(name));
83}83}
8484
85Service::Service(const Bus::Ptr& connection, const std::string& name)85Service::Service(const Bus::Ptr& connection, const std::string& name)

Subscribers

People subscribed via source and target branches