From 41d33117cc3feffe92b95ad0399304145a741ec4 Mon Sep 17 00:00:00 2001 From: Osama Ghanem Date: Sun, 28 Nov 2021 16:30:49 +0200 Subject: [PATCH] Fix #214: Add means to unregister signal handler --- include/sdbus-c++/ConvenienceApiClasses.h | 11 +++++ include/sdbus-c++/ConvenienceApiClasses.inl | 15 ++++++ include/sdbus-c++/IProxy.h | 33 +++++++++++++ src/Proxy.cpp | 10 ++++ src/Proxy.h | 3 ++ tests/integrationtests/DBusSignalsTests.cpp | 47 +++++++++++++++++++ .../integrationtests/integrationtests-proxy.h | 11 +++++ 7 files changed, 130 insertions(+) diff --git a/include/sdbus-c++/ConvenienceApiClasses.h b/include/sdbus-c++/ConvenienceApiClasses.h index 37e8f8b..e533442 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.h +++ b/include/sdbus-c++/ConvenienceApiClasses.h @@ -216,6 +216,17 @@ namespace sdbus { std::string interfaceName_; }; + class SignalUnsubscriber + { + public: + SignalUnsubscriber(IProxy& proxy, const std::string& signalName); + void onInterface(std::string interfaceName); + + private: + IProxy& proxy_; + const std::string& signalName_; + }; + class PropertyGetter { public: diff --git a/include/sdbus-c++/ConvenienceApiClasses.inl b/include/sdbus-c++/ConvenienceApiClasses.inl index 200557e..b528c3f 100644 --- a/include/sdbus-c++/ConvenienceApiClasses.inl +++ b/include/sdbus-c++/ConvenienceApiClasses.inl @@ -671,6 +671,21 @@ namespace sdbus { }); } + /*** ------------------ ***/ + /*** SignalUnsubscriber ***/ + /*** ------------------ ***/ + + inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName) + : proxy_(proxy) + , signalName_(signalName) + { + } + + inline void SignalUnsubscriber::onInterface(std::string interfaceName) + { + proxy_.unregisterSignalHandler(interfaceName, signalName_); + } + /*** -------------- ***/ /*** PropertyGetter ***/ /*** -------------- ***/ diff --git a/include/sdbus-c++/IProxy.h b/include/sdbus-c++/IProxy.h index e55f2b7..539a3ea 100644 --- a/include/sdbus-c++/IProxy.h +++ b/include/sdbus-c++/IProxy.h @@ -143,6 +143,17 @@ namespace sdbus { , const std::string& signalName , signal_handler signalHandler ) = 0; + /*! + * @brief Unregisters the handler of the desired signal + * + * @param[in] interfaceName Name of an interface that the signal belongs to + * @param[in] signalName Name of the signal + * + * @throws sdbus::Error in case of failure + */ + virtual void unregisterSignalHandler( const std::string& interfaceName + , const std::string& signalName ) = 0; + /*! * @brief Finishes the registration of signal handlers * @@ -229,6 +240,23 @@ namespace sdbus { */ [[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName); + /*! + * @brief Unregisters signal handler of a given signal of the proxied D-Bus object + * + * @param[in] signalName Name of the signal + * @return A helper object for convenient unregistration of the signal handler + * + * This is a high-level, convenience way of unregistering a D-Bus signal's handler. + * + * Example of use: + * @code + * object_.muteSignal("fooSignal").onInterface("com.kistler.foo"); + * @endcode + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName); + /*! * @brief Gets value of a property of the proxied D-Bus object * @@ -369,6 +397,11 @@ namespace sdbus { return SignalSubscriber(*this, signalName); } + inline SignalUnsubscriber IProxy::muteSignal(const std::string& signalName) + { + return SignalUnsubscriber(*this, signalName); + } + inline PropertyGetter IProxy::getProperty(const std::string& propertyName) { return PropertyGetter(*this, propertyName); diff --git a/src/Proxy.cpp b/src/Proxy.cpp index c6d0618..55e28a5 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -167,6 +167,16 @@ void Proxy::registerSignalHandler( const std::string& interfaceName SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL); } +void Proxy::unregisterSignalHandler( const std::string& interfaceName + , const std::string& signalName ) +{ + auto& interface = interfaces_[interfaceName]; + + auto removeResult = interface.signals_.erase(signalName); + + SDBUS_THROW_ERROR_IF(removeResult == 0, "Failed to unregister signal handler: handler not exists", EINVAL); +} + void Proxy::finishRegistration() { registerSignalHandlers(*connection_); diff --git a/src/Proxy.h b/src/Proxy.h index 1090aec..7842240 100644 --- a/src/Proxy.h +++ b/src/Proxy.h @@ -58,6 +58,9 @@ namespace sdbus::internal { void registerSignalHandler( const std::string& interfaceName , const std::string& signalName , signal_handler signalHandler ) override; + void unregisterSignalHandler( const std::string& interfaceName + , const std::string& signalName ) override; + void finishRegistration() override; void unregister() override; diff --git a/tests/integrationtests/DBusSignalsTests.cpp b/tests/integrationtests/DBusSignalsTests.cpp index b9dbcc7..9f4267d 100644 --- a/tests/integrationtests/DBusSignalsTests.cpp +++ b/tests/integrationtests/DBusSignalsTests.cpp @@ -114,3 +114,50 @@ TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler) ASSERT_THAT(m_proxy->m_signalMsg, NotNull()); ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal")); } + +TEST_F(SdbusTestObject, UnregisterSignalHandler) +{ + ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler()); + + m_adaptor->emitSimpleSignal(); + + ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal)); +} + +TEST_F(SdbusTestObject, UnregisterSignalHandlerForSomeProxies) +{ + auto proxy1 = std::make_unique(*s_adaptorConnection, BUS_NAME, OBJECT_PATH); + auto proxy2 = std::make_unique(*s_adaptorConnection, BUS_NAME, OBJECT_PATH); + + ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler()); + + m_adaptor->emitSimpleSignal(); + + ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal)); + ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal)); + ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal)); +} + +TEST_F(SdbusTestObject, FailsUnregisterSignalHandlerTwice) +{ + ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler()); + + ASSERT_THROW(m_proxy->unregisterSimpleSignalHandler(), sdbus::Error); +} + +TEST_F(SdbusTestObject, ReRegisterSignalHandler) +{ + // unregister simple-signal handler + ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler()); + + m_adaptor->emitSimpleSignal(); + + ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal)); + + // re-register simple-signal handler + ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler()); + + m_adaptor->emitSimpleSignal(); + + ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal)); +} diff --git a/tests/integrationtests/integrationtests-proxy.h b/tests/integrationtests/integrationtests-proxy.h index ecd11bf..e55d43c 100644 --- a/tests/integrationtests/integrationtests-proxy.h +++ b/tests/integrationtests/integrationtests-proxy.h @@ -169,6 +169,17 @@ public: proxy_.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME); } + void unregisterSimpleSignalHandler() + { + proxy_.muteSignal("simpleSignal").onInterface(INTERFACE_NAME); + } + + void reRegisterSimpleSignalHandler() + { + proxy_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }); + proxy_.finishRegistration(); + } + public: uint32_t action() {