Added flags parameter to register_property method
- flags parameter is set by default to vtable::property_::emits_change
- with this change user can set parameter to vtable::property_::none
to avoid sending property change signal for greater performance
- added example/register-property.cpp with example application
which uses new functions
Tested:
- run openbmc tests with this change, no regression detected
- tested that default behaviour (property_::emits_change) is working as intended
- tested that properties still signal change when emit_change flag is used
- tested that properties doesn't signal change when no emit_change flag is used
- tested that const flag is passed and handled corretly
- verified that performance of application increases when emit_change flag turned off
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
Change-Id: If6187cf076b8e05a5211c83797374a3be94da0c9
diff --git a/example/meson.build b/example/meson.build
index 957c01d..aed2a33 100644
--- a/example/meson.build
+++ b/example/meson.build
@@ -22,6 +22,19 @@
dependencies: [ boost_dep, sdbusplus_dep ],
)
+executable(
+ 'register-property',
+ 'register-property.cpp',
+ cpp_args: [
+ '-DBOOST_ASIO_DISABLE_THREADS',
+ '-DBOOST_ALL_NO_LIB',
+ '-DBOOST_SYSTEM_NO_DEPRECATED',
+ '-DBOOST_ERROR_CODE_HEADER_ONLY',
+ '-DBOOST_COROUTINES_NO_DEPRECATION_WARNING',
+ ],
+ dependencies: [ boost_dep, sdbusplus_dep ],
+)
+
calc_buildroot = meson.current_build_dir()
calc_files = files(
run_command(
diff --git a/example/register-property.cpp b/example/register-property.cpp
new file mode 100644
index 0000000..651e974
--- /dev/null
+++ b/example/register-property.cpp
@@ -0,0 +1,189 @@
+#include <boost/asio.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <iostream>
+
+namespace xyz
+{
+namespace demo
+{
+
+const std::string path = "/xyz/demo";
+const std::string name = "xyz.demo";
+
+} // namespace demo
+} // namespace xyz
+
+namespace name
+{
+
+const std::string greetings = "Greetings";
+const std::string goodbyes = "Goodbyes";
+
+} // namespace name
+
+namespace utils
+{
+
+template <class T>
+class Property
+{
+ public:
+ Property(sdbusplus::asio::connection& bus, std::string_view service,
+ std::string_view path, std::string_view interface,
+ std::string_view name) :
+ bus_(bus),
+ service_(service), path_(path), interface_(interface), name_(name)
+ {}
+
+ template <class F>
+ void async_get(F&& callback)
+ {
+ bus_.async_method_call(
+ [callback =
+ std::move(callback)](const boost::system::error_code& error,
+ const std::variant<T>& valueVariant) {
+ if (error)
+ {
+ callback(std::nullopt);
+ return;
+ }
+
+ if (auto value = std::get_if<T>(&valueVariant))
+ {
+ callback(*value);
+ return;
+ }
+
+ callback(std::nullopt);
+ },
+ service_, path_, "org.freedesktop.DBus.Properties", "Get",
+ interface_, name_);
+ }
+
+ template <class F>
+ void async_set(const T& value, F&& callback)
+ {
+ bus_.async_method_call(
+ [callback = std::move(callback)](
+ const boost::system::error_code& error) { callback(error); },
+ service_, path_, "org.freedesktop.DBus.Properties", "Set",
+ interface_, name_, value);
+ }
+
+ private:
+ sdbusplus::asio::connection& bus_;
+ std::string service_;
+ std::string path_;
+ std::string interface_;
+ std::string name_;
+};
+
+} // namespace utils
+
+class Application
+{
+ public:
+ Application(boost::asio::io_context& ioc, sdbusplus::asio::connection& bus,
+ sdbusplus::asio::object_server& objServer) :
+ ioc_(ioc),
+ bus_(bus), objServer_(objServer)
+ {
+ demo_ = objServer_.add_interface(xyz::demo::path, xyz::demo::name);
+
+ demo_->register_property_r(name::greetings, std::string(),
+ sdbusplus::vtable::property_::const_,
+ [this](const auto&) { return greetings_; });
+
+ demo_->register_property_rw(
+ name::goodbyes, std::string(),
+ sdbusplus::vtable::property_::emits_change,
+ [this](const auto& newPropertyValue, const auto&) {
+ goodbyes_ = newPropertyValue;
+ return 1;
+ },
+ [this](const auto&) { return goodbyes_; });
+
+ demo_->initialize();
+ }
+
+ ~Application()
+ {
+ objServer_.remove_interface(demo_);
+ }
+
+ void asyncReadProperties()
+ {
+ propertyGreetings.async_get([](std::optional<std::string> value) {
+ std::cout << "Greetings value is: "
+ << value.value_or("std::nullopt") << "\n";
+ });
+
+ propertyGoodbyes.async_get([](std::optional<std::string> value) {
+ std::cout << "Goodbyes value is: " << value.value_or("std::nullopt")
+ << "\n";
+ });
+ }
+
+ void asyncChangeProperty()
+ {
+ propertyGreetings.async_set(
+ "Hi, hey, hello", [](const boost::system::error_code& error) {
+ if (error)
+ {
+ std::cout
+ << "As expected, failed to set greetings property\n";
+ }
+ });
+
+ propertyGoodbyes.async_set(
+ "Bye bye", [this](const boost::system::error_code& error) {
+ if (!error)
+ {
+ std::cout << "Changed goodbyes property as expected\n";
+ }
+ boost::asio::post(ioc_, [this] { asyncReadProperties(); });
+ });
+ }
+
+ private:
+ boost::asio::io_context& ioc_;
+ sdbusplus::asio::connection& bus_;
+ sdbusplus::asio::object_server& objServer_;
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> demo_;
+ std::string greetings_ = "Hello";
+ std::string goodbyes_ = "Bye";
+
+ utils::Property<std::string> propertyGreetings{
+ bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
+ name::greetings};
+ utils::Property<std::string> propertyGoodbyes{
+ bus_, xyz::demo::name, xyz::demo::path, xyz::demo::name,
+ name::goodbyes};
+};
+
+int main(int, char**)
+{
+ boost::asio::io_context ioc;
+ boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
+
+ signals.async_wait(
+ [&ioc](const boost::system::error_code&, const int&) { ioc.stop(); });
+
+ auto bus = std::make_shared<sdbusplus::asio::connection>(ioc);
+ auto objServer = std::make_unique<sdbusplus::asio::object_server>(bus);
+
+ bus->request_name(xyz::demo::name.c_str());
+
+ Application app(ioc, *bus, *objServer);
+
+ boost::asio::post(ioc, [&app] { app.asyncReadProperties(); });
+ boost::asio::post(ioc, [&app] { app.asyncChangeProperty(); });
+
+ ioc.run();
+
+ return 0;
+}
diff --git a/include/sdbusplus/asio/object_server.hpp b/include/sdbusplus/asio/object_server.hpp
index b7e161e..f163208 100644
--- a/include/sdbusplus/asio/object_server.hpp
+++ b/include/sdbusplus/asio/object_server.hpp
@@ -375,96 +375,11 @@
std::vector<std::string>{name_});
}
- // default getter and setter
- template <typename PropertyType>
- bool register_property(
- const std::string& name, const PropertyType& property,
- PropertyPermission access = PropertyPermission::readOnly)
- {
- // can only register once
- if (initialized_)
- {
- return false;
- }
- if (!std::regex_match(name, std::regex(PropertyNamePattern)))
- {
- return false;
- }
- static const auto type =
- utility::tuple_to_array(message::types::type_id<PropertyType>());
-
- auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
- auto propertyPtr = std::make_shared<PropertyType>(property);
-
- callbacksGet_[name] = std::make_unique<callback_get_instance<
- PropertyType, std::function<PropertyType(const PropertyType&)>>>(
- propertyPtr, [](const PropertyType& value) { return value; });
- callbacksSet_[name] = std::make_unique<callback_set_instance<
- PropertyType,
- std::function<int(const PropertyType&, PropertyType&)>>>(
- propertyPtr, [](const PropertyType& req, PropertyType& old) {
- old = req;
- return 1;
- });
-
- if (access == PropertyPermission::readOnly)
- {
- vtable_.emplace_back(
- vtable::property(nameItr->c_str(), type.data(), get_handler,
- vtable::property_::emits_change));
- }
- else
- {
- vtable_.emplace_back(
- vtable::property(nameItr->c_str(), type.data(), get_handler,
- set_handler, vtable::property_::emits_change));
- }
- return true;
- }
-
- // custom setter, sets take an input property and respond with an int status
- template <typename PropertyType, typename CallbackType>
- bool register_property(const std::string& name,
- const PropertyType& property,
- CallbackType&& setFunction)
- {
- // can only register once
- if (initialized_)
- {
- return false;
- }
- if (!std::regex_match(name, std::regex(PropertyNamePattern)))
- {
- return false;
- }
- static const auto type =
- utility::tuple_to_array(message::types::type_id<PropertyType>());
-
- auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
- auto propertyPtr = std::make_shared<PropertyType>(property);
-
- callbacksGet_[name] = std::make_unique<callback_get_instance<
- PropertyType, std::function<PropertyType(const PropertyType&)>>>(
- propertyPtr, [](const PropertyType& value) { return value; });
-
- callbacksSet_[name] =
- std::make_unique<callback_set_instance<PropertyType, CallbackType>>(
- propertyPtr, std::move(setFunction));
- vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
- get_handler, set_handler,
- vtable::property_::emits_change));
-
- return true;
- }
-
- // custom getter and setter, gets take an input of void and respond with a
- // property. property is only passed for type deduction
- template <typename PropertyType, typename CallbackType,
- typename CallbackTypeGet>
- bool register_property(const std::string& name,
- const PropertyType& property,
- CallbackType&& setFunction,
- CallbackTypeGet&& getFunction)
+ template <typename PropertyType, typename CallbackTypeGet>
+ bool register_property_r(const std::string& name,
+ const PropertyType& property,
+ decltype(vtable::vtable_t::flags) flags,
+ CallbackTypeGet&& getFunction)
{
// can only register once
if (initialized_)
@@ -484,16 +399,107 @@
callbacksGet_[name] = std::make_unique<
callback_get_instance<PropertyType, CallbackTypeGet>>(
propertyPtr, std::move(getFunction));
- callbacksSet_[name] =
- std::make_unique<callback_set_instance<PropertyType, CallbackType>>(
- propertyPtr, std::move(setFunction));
+ callbacksSet_[name] = std::make_unique<callback_set_instance<
+ PropertyType,
+ std::function<int(const PropertyType&, PropertyType&)>>>(
+ propertyPtr, [](const PropertyType& req, PropertyType& old) {
+ old = req;
+ return 1;
+ });
vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
- get_handler, set_handler,
- vtable::property_::emits_change));
+ get_handler, flags));
return true;
}
+
+ template <typename PropertyType, typename CallbackTypeSet,
+ typename CallbackTypeGet>
+ bool register_property_rw(const std::string& name,
+ const PropertyType& property,
+ decltype(vtable::vtable_t::flags) flags,
+ CallbackTypeSet&& setFunction,
+ CallbackTypeGet&& getFunction)
+ {
+ // can only register once
+ if (initialized_)
+ {
+ return false;
+ }
+ if (!std::regex_match(name, std::regex(PropertyNamePattern)))
+ {
+ return false;
+ }
+ static const auto type =
+ utility::tuple_to_array(message::types::type_id<PropertyType>());
+
+ auto nameItr = propertyNames_.emplace(propertyNames_.end(), name);
+ auto propertyPtr = std::make_shared<PropertyType>(property);
+
+ callbacksGet_[name] = std::make_unique<
+ callback_get_instance<PropertyType, CallbackTypeGet>>(
+ propertyPtr, std::move(getFunction));
+ callbacksSet_[name] = std::make_unique<
+ callback_set_instance<PropertyType, CallbackTypeSet>>(
+ propertyPtr, std::move(setFunction));
+
+ vtable_.emplace_back(vtable::property(nameItr->c_str(), type.data(),
+ get_handler, set_handler, flags));
+
+ return true;
+ }
+
+ // default getter and setter
+ template <typename PropertyType>
+ bool register_property(
+ const std::string& name, const PropertyType& property,
+ PropertyPermission access = PropertyPermission::readOnly)
+ {
+ if (access == PropertyPermission::readOnly)
+ {
+ return register_property_r(
+ name, property, vtable::property_::emits_change,
+ [](const PropertyType& value) { return value; });
+ }
+ else
+ {
+ return register_property_rw(
+ name, property, vtable::property_::emits_change,
+ [](const PropertyType& req, PropertyType& old) {
+ old = req;
+ return 1;
+ },
+ [](const PropertyType& value) { return value; });
+ }
+ }
+
+ // custom setter, sets take an input property and respond with an int status
+ template <typename PropertyType, typename CallbackTypeSet>
+ bool register_property(const std::string& name,
+ const PropertyType& property,
+ CallbackTypeSet&& setFunction)
+ {
+ return register_property_rw(
+ name, property, vtable::property_::emits_change,
+ std::forward<CallbackTypeSet>(setFunction),
+ [](const PropertyType& value) { return value; });
+ }
+
+ // custom getter and setter, gets take an input of void and respond with a
+ // property. property is only passed for type deduction
+ template <typename PropertyType, typename CallbackTypeSet,
+ typename CallbackTypeGet>
+ bool register_property(const std::string& name,
+ const PropertyType& property,
+ CallbackTypeSet&& setFunction,
+ CallbackTypeGet&& getFunction)
+ {
+ return register_property_rw(name, property,
+ vtable::property_::emits_change,
+ std::forward<CallbackTypeSet>(setFunction),
+ std::forward<CallbackTypeGet>(getFunction));
+ }
+
template <typename PropertyType, bool changesOnly = false>
bool set_property(const std::string& name, const PropertyType& value)
{
diff --git a/include/sdbusplus/vtable.hpp b/include/sdbusplus/vtable.hpp
index 3a0bdb7..09dbfc6 100644
--- a/include/sdbusplus/vtable.hpp
+++ b/include/sdbusplus/vtable.hpp
@@ -116,6 +116,7 @@
constexpr auto emits_change = SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
constexpr auto emits_invalidation = SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
constexpr auto explicit_ = SD_BUS_VTABLE_PROPERTY_EXPLICIT;
+constexpr auto none = decltype(vtable_t::flags){};
} // namespace property_
constexpr vtable_t start(decltype(vtable_t::flags) flags)