refactor: improve Proxy signal subscription (#389)

This makes D-Bus proxy signal registration more flexible, more dynamic, and less error-prone since no `finishRegistration()` call is needed. A proxy can register to a signal at any time during its lifetime, and can unregister freely by simply destroying the associated slot.
This commit is contained in:
Stanislav Angelovič
2023-12-30 21:11:02 +01:00
committed by Stanislav Angelovic
parent e3040c0998
commit bdd0bc2c27
17 changed files with 227 additions and 202 deletions
+35 -28
View File
@@ -32,10 +32,9 @@
#include SDBUS_HEADER
#include <string>
#include <memory>
#include <map>
#include <deque>
#include <vector>
#include <mutex>
#include <condition_variable>
namespace sdbus::internal {
@@ -63,10 +62,10 @@ 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;
Slot registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler
, request_slot_t ) override;
void unregister() override;
sdbus::IConnection& getConnection() const override;
@@ -74,9 +73,8 @@ namespace sdbus::internal {
Message getCurrentlyProcessedMessage() const override;
private:
void registerSignalHandlers(sdbus::internal::IConnection& connection);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
friend PendingAsyncCall;
@@ -87,28 +85,37 @@ namespace sdbus::internal {
std::string destination_;
std::string objectPath_;
using InterfaceName = std::string;
struct InterfaceData
std::vector<Slot> floatingSignalSlots_;
struct SignalInfo
{
using SignalName = std::string;
struct SignalData
{
SignalData(Proxy& proxy, signal_handler callback, Slot slot)
: proxy(proxy)
, callback(std::move(callback))
, slot(std::move(slot))
{}
Proxy& proxy;
signal_handler callback;
// slot must be listed after callback to ensure that slot is destructed first.
// Destructing the slot will sd_bus_slot_unref() the callback.
// Only after sd_bus_slot_unref(), we can safely delete the callback. The bus mutex (SdBus::sdbusMutex_)
// ensures that sd_bus_slot_unref() and the callback execute sequentially.
Slot slot;
};
std::map<SignalName, std::unique_ptr<SignalData>> signals_;
signal_handler callback;
Proxy& proxy;
Slot slot;
};
std::map<InterfaceName, InterfaceData> interfaces_;
// using InterfaceName = std::string;
// struct InterfaceData
// {
// using SignalName = std::string;
// struct SignalData
// {
// SignalData(Proxy& proxy, signal_handler callback, Slot slot)
// : proxy(proxy)
// , callback(std::move(callback))
// , slot(std::move(slot))
// {}
// Proxy& proxy;
// signal_handler callback;
// // slot must be listed after callback to ensure that slot is destructed first.
// // Destructing the slot will sd_bus_slot_unref() the callback.
// // Only after sd_bus_slot_unref(), we can safely delete the callback. The bus mutex (SdBus::sdbusMutex_)
// // ensures that sd_bus_slot_unref() and the callback execute sequentially.
// Slot slot;
// };
// std::map<SignalName, std::unique_ptr<SignalData>> signals_;
// };
// std::map<InterfaceName, InterfaceData> interfaces_;
// We need to keep track of pending async calls. When the proxy is being destructed, we must
// remove all slots of these pending calls, otherwise in case when the connection outlives