diff --git a/include/sdbus-c++/IConnection.h b/include/sdbus-c++/IConnection.h index 854414e..5ad3216 100644 --- a/include/sdbus-c++/IConnection.h +++ b/include/sdbus-c++/IConnection.h @@ -36,6 +36,9 @@ struct sd_bus; struct sd_event; +namespace sdbus { + class Message; +} namespace sdbus { @@ -209,6 +212,21 @@ namespace sdbus { */ virtual bool processPendingEvent() = 0; + /*! + * @brief Provides access to the currently processed D-Bus message + * + * This method provides access to the currently processed incoming D-Bus message. + * "Currently processed" means that the registered callback handler(s) for that message + * are being invoked. This method is meant to be called from within a callback handler + * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. + * + * @return Currently processed D-Bus message + */ + virtual Message getCurrentlyProcessedMessage() const = 0; + /*! * @brief Sets general method call timeout * diff --git a/include/sdbus-c++/IObject.h b/include/sdbus-c++/IObject.h index e567586..9422eeb 100644 --- a/include/sdbus-c++/IObject.h +++ b/include/sdbus-c++/IObject.h @@ -442,20 +442,19 @@ namespace sdbus { virtual const std::string& getObjectPath() const = 0; /*! - * @brief Provides currently processed D-Bus message + * @brief Provides access to the currently processed D-Bus message * - * This method provides immutable access to the currently processed incoming D-Bus message. + * This method provides access to the currently processed incoming D-Bus message. * "Currently processed" means that the registered callback handler(s) for that message * are being invoked. This method is meant to be called from within a callback handler - * (e.g. D-Bus method implementation handler). In such a case it is guaranteed to return - * a valid pointer to the D-Bus message for which the handler is called. If called from other - * contexts/threads, it may return a nonzero pointer or a nullptr, depending on whether a message - * was processed at the time of call or not, but the value is nondereferencable, since the pointed-to - * message may have gone in the meantime. + * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. * - * @return A pointer to the currently processed D-Bus message + * @return Currently processed D-Bus message */ - virtual const Message* getCurrentlyProcessedMessage() const = 0; + virtual Message getCurrentlyProcessedMessage() const = 0; }; // Out-of-line member definitions diff --git a/include/sdbus-c++/IProxy.h b/include/sdbus-c++/IProxy.h index 4a89e0f..4ba39e8 100644 --- a/include/sdbus-c++/IProxy.h +++ b/include/sdbus-c++/IProxy.h @@ -92,7 +92,7 @@ namespace sdbus { * The call does not block if the method call has dont-expect-reply flag set. In that case, * the call returns immediately and the return value is an empty, invalid method reply. * - * The call blocks otherwise, waiting for the remote peer to send back a reply, or an error, + * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, * or until the call times out. * * While blocking, other concurrent operations (in other threads) on the underlying bus @@ -102,8 +102,8 @@ namespace sdbus { * callMethod() function overload, which does not block the bus connection, or do the synchronous * call from another Proxy instance created just before the call and then destroyed (which is * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have - * its own bus connection. Slim proxies created with `dont_run_event_loop_thread` tag are - * designed for exactly that purpose. + * its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread` + * tag are designed for exactly that purpose. * * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. * @@ -125,7 +125,9 @@ namespace sdbus { * @param[in] timeout Timeout for dbus call in microseconds * @return Cookie for the the pending asynchronous call * - * The call is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, * the provided async reply handler will get invoked from the context of the bus * connection I/O event loop thread. * @@ -141,6 +143,53 @@ namespace sdbus { template PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout); + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethod(const MethodCall& message, with_future_t) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] timeout Method call timeout + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception, or the call timed out). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethod(const MethodCall& message, uint64_t timeout, with_future_t) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t) + */ + template + std::future callMethod( const MethodCall& message + , const std::chrono::duration<_Rep, _Period>& timeout + , with_future_t ); + /*! * @brief Registers a handler for the desired signal emitted by the D-Bus object * @@ -397,47 +446,19 @@ namespace sdbus { virtual const std::string& getObjectPath() const = 0; /*! - * @brief Provides currently processed D-Bus message + * @brief Provides access to the currently processed D-Bus message * - * This method provides immutable access to the currently processed incoming D-Bus message. + * This method provides access to the currently processed incoming D-Bus message. * "Currently processed" means that the registered callback handler(s) for that message * are being invoked. This method is meant to be called from within a callback handler * (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is - * guaranteed to return a valid pointer to the D-Bus message for which the handler is called. - * If called from other contexts/threads, it may return a nonzero pointer or a nullptr, depending - * on whether a message was processed at the time of call or not, but the value is nondereferencable, - * since the pointed-to message may have gone in the meantime. + * guaranteed to return a valid D-Bus message instance for which the handler is called. + * If called from other contexts/threads, it may return a valid or invalid message, depending + * on whether a message was processed or not at the time of the call. * - * @return A pointer to the currently processed D-Bus message + * @return Currently processed D-Bus message */ - virtual const Message* getCurrentlyProcessedMessage() const = 0; - - /*! - * @brief Calls method on the D-Bus object asynchronously - * - * @param[in] message Message representing an async method call - * @param[in] asyncReplyCallback Handler for the async reply - * @param[in] timeout Timeout for dbus call in microseconds - * @return Cookie for the the pending asynchronous call - * - * The call is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided async reply handler will get invoked from the context of the connection - * I/O event loop thread. - * - * Note: To avoid messing with messages, use higher-level API defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual std::future callMethod(const MethodCall& message, with_future_t) = 0; - virtual std::future callMethod(const MethodCall& message, uint64_t timeout, with_future_t) = 0; - - /*! - * @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t) - */ - template - std::future callMethod( const MethodCall& message - , const std::chrono::duration<_Rep, _Period>& timeout - , with_future_t ); + virtual Message getCurrentlyProcessedMessage() const = 0; }; /********************************************//** diff --git a/include/sdbus-c++/Message.h b/include/sdbus-c++/Message.h index 50fd528..14d04ad 100644 --- a/include/sdbus-c++/Message.h +++ b/include/sdbus-c++/Message.h @@ -70,13 +70,19 @@ namespace sdbus { * Serialization and deserialization functions are provided for types supported * by D-Bus. * - * You don't need to work with this class directly if you use high-level APIs - * of @c IObject and @c IProxy. + * You mostly don't need to work with this class directly if you use high-level + * APIs of @c IObject and @c IProxy. * ***********************************************/ class [[nodiscard]] Message { public: + Message(const Message&) noexcept; + Message& operator=(const Message&) noexcept; + Message(Message&& other) noexcept; + Message& operator=(Message&& other) noexcept; + ~Message(); + Message& operator<<(bool item); Message& operator<<(int16_t item); Message& operator<<(int32_t item); @@ -222,13 +228,6 @@ namespace sdbus { Message(void *msg, internal::ISdBus* sdbus) noexcept; Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept; - Message(const Message&) noexcept; - Message& operator=(const Message&) noexcept; - Message(Message&& other) noexcept; - Message& operator=(Message&& other) noexcept; - - ~Message(); - friend Factory; protected: diff --git a/src/Connection.cpp b/src/Connection.cpp index 8120f67..d2ad127 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -774,6 +774,13 @@ bool Connection::arePendingMessagesInReadQueue() const return readQueueSize > 0; } +Message Connection::getCurrentlyProcessedMessage() const +{ + auto* sdbusMsg = sdbus_->sd_bus_get_current_message(bus_.get()); + + return Message::Factory::create(sdbusMsg, sdbus_.get()); +} + std::string Connection::composeSignalMatchFilter( const std::string &sender , const std::string &objectPath , const std::string &interfaceName diff --git a/src/Connection.h b/src/Connection.h index b42c574..721c3f4 100644 --- a/src/Connection.h +++ b/src/Connection.h @@ -86,6 +86,7 @@ namespace sdbus::internal { void leaveEventLoop() override; PollData getEventLoopPollData() const override; bool processPendingEvent() override; + Message getCurrentlyProcessedMessage() const override; void addObjectManager(const std::string& objectPath) override; void addObjectManager(const std::string& objectPath, floating_slot_t) override; diff --git a/src/ISdBus.h b/src/ISdBus.h index e38aa29..a8efe94 100644 --- a/src/ISdBus.h +++ b/src/ISdBus.h @@ -87,6 +87,7 @@ namespace sdbus::internal { virtual int sd_bus_start(sd_bus *bus) = 0; virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0; + virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) = 0; virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0; virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) = 0; virtual int sd_bus_flush(sd_bus *bus) = 0; diff --git a/src/Object.cpp b/src/Object.cpp index 097da5a..66ff867 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -240,9 +240,9 @@ const std::string& Object::getObjectPath() const return objectPath_; } -const Message* Object::getCurrentlyProcessedMessage() const +Message Object::getCurrentlyProcessedMessage() const { - return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed); + return connection_.getCurrentlyProcessedMessage(); } Object::InterfaceData& Object::getInterface(const std::string& interfaceName) @@ -338,12 +338,6 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, auto message = Message::Factory::create(sdbusMessage, &object.connection_.getSdBusInterface()); - object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed); - SCOPE_EXIT - { - object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed); - }; - auto& callback = interfaceData->methods[message.getMemberName()].callback; assert(callback); @@ -396,12 +390,6 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/ auto value = Message::Factory::create(sdbusValue, &object.connection_.getSdBusInterface()); - object.m_CurrentlyProcessedMessage.store(&value, std::memory_order_relaxed); - SCOPE_EXIT - { - object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed); - }; - auto ok = invokeHandlerAndCatchErrors([&](){ callback(value); }, retError); return ok ? 1 : -1; diff --git a/src/Object.h b/src/Object.h index d38b1c0..50cd7b9 100644 --- a/src/Object.h +++ b/src/Object.h @@ -35,7 +35,6 @@ #include #include #include -#include #include namespace sdbus::internal { @@ -103,7 +102,7 @@ namespace sdbus::internal { sdbus::IConnection& getConnection() const override; const std::string& getObjectPath() const override; - const Message* getCurrentlyProcessedMessage() const override; + Message getCurrentlyProcessedMessage() const override; private: using InterfaceName = std::string; @@ -178,7 +177,6 @@ namespace sdbus::internal { std::string objectPath_; std::map interfaces_; Slot objectManagerSlot_; - std::atomic m_CurrentlyProcessedMessage{nullptr}; }; } diff --git a/src/Proxy.cpp b/src/Proxy.cpp index fd91dfe..fab35d2 100644 --- a/src/Proxy.cpp +++ b/src/Proxy.cpp @@ -200,9 +200,9 @@ const std::string& Proxy::getObjectPath() const return objectPath_; } -const Message* Proxy::getCurrentlyProcessedMessage() const +Message Proxy::getCurrentlyProcessedMessage() const { - return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed); + return connection_->getCurrentlyProcessedMessage(); } int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError) @@ -225,12 +225,6 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat auto message = Message::Factory::create(sdbusMessage, &proxy.connection_->getSdBusInterface()); - proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed); - SCOPE_EXIT - { - proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed); - }; - auto ok = invokeHandlerAndCatchErrors([&] { const auto* error = sd_bus_message_get_error(sdbusMessage); @@ -256,12 +250,6 @@ int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd auto message = Message::Factory::create(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface()); - signalData->proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed); - SCOPE_EXIT - { - signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed); - }; - auto ok = invokeHandlerAndCatchErrors([&](){ signalData->callback(message); }, retError); return ok ? 0 : -1; diff --git a/src/Proxy.h b/src/Proxy.h index 13b2e28..53d81bf 100644 --- a/src/Proxy.h +++ b/src/Proxy.h @@ -35,7 +35,6 @@ #include #include #include -#include #include namespace sdbus::internal { @@ -72,7 +71,7 @@ namespace sdbus::internal { sdbus::IConnection& getConnection() const override; const std::string& getObjectPath() const override; - const Message* getCurrentlyProcessedMessage() const override; + Message getCurrentlyProcessedMessage() const override; private: void registerSignalHandlers(sdbus::internal::IConnection& connection); @@ -178,8 +177,6 @@ namespace sdbus::internal { std::mutex mutex_; std::deque> calls_; } pendingAsyncCalls_; - - std::atomic m_CurrentlyProcessedMessage{nullptr}; }; } diff --git a/src/SdBus.cpp b/src/SdBus.cpp index 728add2..47fa682 100644 --- a/src/SdBus.cpp +++ b/src/SdBus.cpp @@ -382,6 +382,11 @@ int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r) return ::sd_bus_process(bus, r); } +sd_bus_message* SdBus::sd_bus_get_current_message(sd_bus *bus) +{ + return ::sd_bus_get_current_message(bus); +} + int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data) { std::lock_guard lock(sdbusMutex_); diff --git a/src/SdBus.h b/src/SdBus.h index dc91b37..1ab9e81 100644 --- a/src/SdBus.h +++ b/src/SdBus.h @@ -79,6 +79,7 @@ public: virtual int sd_bus_start(sd_bus *bus) override; virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override; + virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override; virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override; virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) override; virtual int sd_bus_flush(sd_bus *bus) override; diff --git a/tests/integrationtests/TestAdaptor.cpp b/tests/integrationtests/TestAdaptor.cpp index 6a8ff18..9a13995 100644 --- a/tests/integrationtests/TestAdaptor.cpp +++ b/tests/integrationtests/TestAdaptor.cpp @@ -122,7 +122,7 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param) { std::this_thread::sleep_for(std::chrono::milliseconds(param)); - m_methodCallMsg = getObject().getCurrentlyProcessedMessage(); + m_methodCallMsg = std::make_unique(getObject().getCurrentlyProcessedMessage()); m_methodCallMemberName = m_methodCallMsg->getMemberName(); return param; @@ -130,7 +130,7 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param) void TestAdaptor::doOperationAsync(sdbus::Result&& result, uint32_t param) { - m_methodCallMsg = getObject().getCurrentlyProcessedMessage(); + m_methodCallMsg = std::make_unique(getObject().getCurrentlyProcessedMessage()); m_methodCallMemberName = m_methodCallMsg->getMemberName(); if (param == 0) @@ -234,7 +234,7 @@ bool TestAdaptor::blocking() void TestAdaptor::blocking(const bool& value) { - m_propertySetMsg = getObject().getCurrentlyProcessedMessage(); + m_propertySetMsg = std::make_unique(getObject().getCurrentlyProcessedMessage()); m_propertySetSender = m_propertySetMsg->getSender(); m_blocking = value; diff --git a/tests/integrationtests/TestAdaptor.h b/tests/integrationtests/TestAdaptor.h index e2bcfb4..2ed31fa 100644 --- a/tests/integrationtests/TestAdaptor.h +++ b/tests/integrationtests/TestAdaptor.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace sdbus { namespace test { @@ -103,9 +104,9 @@ public: // for tests mutable double m_multiplyResult{}; mutable std::atomic m_wasThrowErrorCalled{false}; - const Message* m_methodCallMsg{}; + std::unique_ptr m_methodCallMsg; std::string m_methodCallMemberName; - const Message* m_propertySetMsg{}; + std::unique_ptr m_propertySetMsg; std::string m_propertySetSender; }; diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 1d8908b..f20cbc3 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -61,7 +61,7 @@ TestProxy::~TestProxy() void TestProxy::onSimpleSignal() { - m_signalMsg = getProxy().getCurrentlyProcessedMessage(); + m_signalMsg = std::make_unique(getProxy().getCurrentlyProcessedMessage()); m_signalMemberName = m_signalMsg->getMemberName(); m_gotSimpleSignal = true; diff --git a/tests/integrationtests/TestProxy.h b/tests/integrationtests/TestProxy.h index 72a50e8..cebbce8 100644 --- a/tests/integrationtests/TestProxy.h +++ b/tests/integrationtests/TestProxy.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace sdbus { namespace test { @@ -118,7 +119,7 @@ public: // for tests std::function m_DoOperationClientSideAsyncReplyHandler; std::function&, const std::vector&)> m_onPropertiesChangedHandler; - const Message* m_signalMsg{}; + std::unique_ptr m_signalMsg; std::string m_signalMemberName; }; diff --git a/tests/unittests/mocks/SdBusMock.h b/tests/unittests/mocks/SdBusMock.h index 587b8d9..353b74d 100644 --- a/tests/unittests/mocks/SdBusMock.h +++ b/tests/unittests/mocks/SdBusMock.h @@ -78,6 +78,7 @@ public: MOCK_METHOD1(sd_bus_start, int(sd_bus *bus)); MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r)); + MOCK_METHOD1(sd_bus_get_current_message, sd_bus_message*(sd_bus *bus)); MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data)); MOCK_METHOD2(sd_bus_get_n_queued_read, int(sd_bus *bus, uint64_t *ret)); MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus));