forked from Kistler-Group/sdbus-cpp
feat: add support for match rules
This commit is contained in:
committed by
Stanislav Angelovič
parent
d864e1dfa4
commit
5ec6027d5f
@ -19,7 +19,8 @@ Using sdbus-c++ library
|
|||||||
14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
|
14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
|
||||||
15. [Using D-Bus properties](#using-d-bus-properties)
|
15. [Using D-Bus properties](#using-d-bus-properties)
|
||||||
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
|
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
|
||||||
17. [Conclusion](#conclusion)
|
17. [Support for match rules](#support-for-match-rules)
|
||||||
|
18. [Conclusion](#conclusion)
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
@ -1279,6 +1280,11 @@ Note that signals of afore-mentioned standard D-Bus interfaces are not emitted b
|
|||||||
|
|
||||||
Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory.
|
Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory.
|
||||||
|
|
||||||
|
Support for match rules
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
`IConnection` class provides `addMatch` method that you can use to install match rules. An associated callback handler will be called upon an incoming message matching given match rule. There is support for both client-owned and floating (library-owned) match rules. Consult `IConnection` header or sdbus-c++ doxygen documentation for more information.
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
41
include/sdbus-c++/IConnection.h
Normal file → Executable file
41
include/sdbus-c++/IConnection.h
Normal file → Executable file
@ -265,6 +265,47 @@ namespace sdbus {
|
|||||||
*/
|
*/
|
||||||
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
|
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Adds a match rule for incoming message dispatching
|
||||||
|
*
|
||||||
|
* @param[in] match Match expression to filter incoming D-Bus message
|
||||||
|
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
|
||||||
|
* @return RAII-style slot handle representing the ownership of the subscription
|
||||||
|
*
|
||||||
|
* The method installs a match rule for messages received on the specified bus connection.
|
||||||
|
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
|
||||||
|
* The specified handler function callback is called for each incoming message matching the specified
|
||||||
|
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
|
||||||
|
* sends a control message requested the match to be added to the broker and waits until the broker
|
||||||
|
* confirms the match has been installed successfully.
|
||||||
|
*
|
||||||
|
* Simply let go of the slot instance to uninstall the match rule from the bus connection. The slot
|
||||||
|
* must not outlive the connection for the slot is associated with it.
|
||||||
|
*
|
||||||
|
* For more information, consult `man sd_bus_add_match`.
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Adds a floating match rule for incoming message dispatching
|
||||||
|
*
|
||||||
|
* @param[in] match Match expression to filter incoming D-Bus message
|
||||||
|
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
|
||||||
|
* @param[in] Floating slot tag
|
||||||
|
*
|
||||||
|
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||||
|
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||||
|
* is bound to the lifetime of the bus connection.
|
||||||
|
*
|
||||||
|
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation for more
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* @throws sdbus::Error in case of failure
|
||||||
|
*/
|
||||||
|
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @copydoc IConnection::enterEventLoop()
|
* @copydoc IConnection::enterEventLoop()
|
||||||
*
|
*
|
||||||
|
2
include/sdbus-c++/TypeTraits.h
Normal file → Executable file
2
include/sdbus-c++/TypeTraits.h
Normal file → Executable file
@ -46,6 +46,7 @@ namespace sdbus {
|
|||||||
class MethodCall;
|
class MethodCall;
|
||||||
class MethodReply;
|
class MethodReply;
|
||||||
class Signal;
|
class Signal;
|
||||||
|
class Message;
|
||||||
class PropertySetCall;
|
class PropertySetCall;
|
||||||
class PropertyGetReply;
|
class PropertyGetReply;
|
||||||
template <typename... _Results> class Result;
|
template <typename... _Results> class Result;
|
||||||
@ -58,6 +59,7 @@ namespace sdbus {
|
|||||||
using method_callback = std::function<void(MethodCall msg)>;
|
using method_callback = std::function<void(MethodCall msg)>;
|
||||||
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
|
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
|
||||||
using signal_handler = std::function<void(Signal& signal)>;
|
using signal_handler = std::function<void(Signal& signal)>;
|
||||||
|
using message_handler = std::function<void(Message& msg)>;
|
||||||
using property_set_callback = std::function<void(PropertySetCall& msg)>;
|
using property_set_callback = std::function<void(PropertySetCall& msg)>;
|
||||||
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
|
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
|
||||||
|
|
||||||
|
28
src/Connection.cpp
Normal file → Executable file
28
src/Connection.cpp
Normal file → Executable file
@ -183,6 +183,34 @@ uint64_t Connection::getMethodCallTimeout() const
|
|||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Slot Connection::addMatch(const std::string& match, message_handler callback)
|
||||||
|
{
|
||||||
|
auto matchInfo = std::make_unique<MatchInfo>(MatchInfo{std::move(callback), *this, {}});
|
||||||
|
|
||||||
|
auto messageHandler = [](sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) -> int
|
||||||
|
{
|
||||||
|
auto* matchInfo = static_cast<MatchInfo*>(userData);
|
||||||
|
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||||
|
matchInfo->callback(message);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto r = iface_->sd_bus_add_match(bus_.get(), &matchInfo->slot, match.c_str(), std::move(messageHandler), matchInfo.get());
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add match", -r);
|
||||||
|
|
||||||
|
return {matchInfo.release(), [this](void *ptr)
|
||||||
|
{
|
||||||
|
auto* matchInfo = static_cast<MatchInfo*>(ptr);
|
||||||
|
iface_->sd_bus_slot_unref(matchInfo->slot);
|
||||||
|
std::default_delete<MatchInfo>{}(matchInfo);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::addMatch(const std::string& match, message_handler callback, floating_slot_t)
|
||||||
|
{
|
||||||
|
floatingMatchRules_.push_back(addMatch(match, std::move(callback)));
|
||||||
|
}
|
||||||
|
|
||||||
Slot Connection::addObjectVTable( const std::string& objectPath
|
Slot Connection::addObjectVTable( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const sd_bus_vtable* vtable
|
, const sd_bus_vtable* vtable
|
||||||
|
11
src/Connection.h
Normal file → Executable file
11
src/Connection.h
Normal file → Executable file
@ -74,6 +74,9 @@ namespace sdbus::internal {
|
|||||||
void setMethodCallTimeout(uint64_t timeout) override;
|
void setMethodCallTimeout(uint64_t timeout) override;
|
||||||
uint64_t getMethodCallTimeout() const override;
|
uint64_t getMethodCallTimeout() const override;
|
||||||
|
|
||||||
|
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
|
||||||
|
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
|
||||||
|
|
||||||
const ISdBus& getSdBusInterface() const override;
|
const ISdBus& getSdBusInterface() const override;
|
||||||
ISdBus& getSdBusInterface() override;
|
ISdBus& getSdBusInterface() override;
|
||||||
|
|
||||||
@ -138,6 +141,13 @@ namespace sdbus::internal {
|
|||||||
int fd{-1};
|
int fd{-1};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MatchInfo
|
||||||
|
{
|
||||||
|
message_handler callback;
|
||||||
|
Connection& connection;
|
||||||
|
sd_bus_slot *slot;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ISdBus> iface_;
|
std::unique_ptr<ISdBus> iface_;
|
||||||
BusPtr bus_;
|
BusPtr bus_;
|
||||||
@ -147,6 +157,7 @@ namespace sdbus::internal {
|
|||||||
EventFd loopExitFd_;
|
EventFd loopExitFd_;
|
||||||
EventFd eventFd_;
|
EventFd eventFd_;
|
||||||
std::atomic<uint64_t> activeTimeout_{};
|
std::atomic<uint64_t> activeTimeout_{};
|
||||||
|
std::vector<Slot> floatingMatchRules_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
|||||||
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "TestAdaptor.h"
|
#include "TestAdaptor.h"
|
||||||
#include "TestProxy.h"
|
#include "TestProxy.h"
|
||||||
|
#include "TestFixture.h"
|
||||||
#include "sdbus-c++/sdbus-c++.h"
|
#include "sdbus-c++/sdbus-c++.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@ -38,8 +39,13 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using namespace std::chrono_literals;
|
||||||
using namespace sdbus::test;
|
using namespace sdbus::test;
|
||||||
|
|
||||||
|
using AConnection = TestFixture;
|
||||||
|
|
||||||
/*-------------------------------------*/
|
/*-------------------------------------*/
|
||||||
/* -- TEST CASES -- */
|
/* -- TEST CASES -- */
|
||||||
/*-------------------------------------*/
|
/*-------------------------------------*/
|
||||||
@ -52,3 +58,75 @@ TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
|||||||
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
|
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
|
||||||
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
|
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
|
||||||
|
{
|
||||||
|
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||||
|
std::atomic<bool> matchingMessageReceived{false};
|
||||||
|
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||||
|
{
|
||||||
|
if(msg.getPath() == OBJECT_PATH)
|
||||||
|
matchingMessageReceived = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
|
||||||
|
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
|
||||||
|
{
|
||||||
|
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||||
|
std::atomic<bool> matchingMessageReceived{false};
|
||||||
|
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||||
|
{
|
||||||
|
if(msg.getPath() == OBJECT_PATH)
|
||||||
|
matchingMessageReceived = true;
|
||||||
|
});
|
||||||
|
slot.reset();
|
||||||
|
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
|
||||||
|
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AConnection, CanAddFloatingMatchRule)
|
||||||
|
{
|
||||||
|
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||||
|
std::atomic<bool> matchingMessageReceived{false};
|
||||||
|
auto con = sdbus::createSystemBusConnection();
|
||||||
|
con->enterEventLoopAsync();
|
||||||
|
auto callback = [&](sdbus::Message& msg)
|
||||||
|
{
|
||||||
|
if(msg.getPath() == OBJECT_PATH)
|
||||||
|
matchingMessageReceived = true;
|
||||||
|
};
|
||||||
|
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
assert(waitUntil(matchingMessageReceived, 2s));
|
||||||
|
matchingMessageReceived = false;
|
||||||
|
|
||||||
|
con.reset();
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
|
||||||
|
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
|
||||||
|
{
|
||||||
|
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
|
||||||
|
std::atomic<size_t> numberOfMatchingMessages{};
|
||||||
|
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||||
|
{
|
||||||
|
if(msg.getMemberName() == "simpleSignal")
|
||||||
|
numberOfMatchingMessages++;
|
||||||
|
});
|
||||||
|
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
|
||||||
|
|
||||||
|
m_adaptor->emitSignalWithMap({});
|
||||||
|
adaptor2->emitSimpleSignal();
|
||||||
|
m_adaptor->emitSimpleSignal();
|
||||||
|
|
||||||
|
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
|
||||||
|
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user