diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c755fb..8c55ef9 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ set(SDBUSCPP_CPP_SRCS ${SDBUSCPP_SOURCE_DIR}/ConvenienceClasses.cpp ${SDBUSCPP_SOURCE_DIR}/Error.cpp ${SDBUSCPP_SOURCE_DIR}/Message.cpp - ${SDBUSCPP_SOURCE_DIR}/MethodResult.cpp ${SDBUSCPP_SOURCE_DIR}/Object.cpp ${SDBUSCPP_SOURCE_DIR}/ObjectProxy.cpp ${SDBUSCPP_SOURCE_DIR}/Types.cpp @@ -58,10 +57,11 @@ set(SDBUSCPP_PUBLIC_HDRS ${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h ${SDBUSCPP_INCLUDE_DIR}/Message.h ${SDBUSCPP_INCLUDE_DIR}/MethodResult.h - ${SDBUSCPP_INCLUDE_DIR}/sdbus-c++.h + ${SDBUSCPP_INCLUDE_DIR}/MethodResult.inl ${SDBUSCPP_INCLUDE_DIR}/Types.h ${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h - ${SDBUSCPP_INCLUDE_DIR}/Flags.h) + ${SDBUSCPP_INCLUDE_DIR}/Flags.h + ${SDBUSCPP_INCLUDE_DIR}/sdbus-c++.h) set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HDRS}) @@ -94,6 +94,7 @@ set_target_properties(sdbus-c++ VERSION "${SDBUSCPP_VERSION}" SOVERSION "${SDBUSCPP_VERSION_MAJOR}" OUTPUT_NAME "sdbus-c++") + target_link_libraries(sdbus-c++ ${SYSTEMD_LIBRARIES}) #---------------------------------- diff --git a/include/sdbus-c++/ConvenienceClasses.h b/include/sdbus-c++/ConvenienceClasses.h index e319772..64372ad 100644 --- a/include/sdbus-c++/ConvenienceClasses.h +++ b/include/sdbus-c++/ConvenienceClasses.h @@ -183,7 +183,6 @@ namespace sdbus { AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName); AsyncMethodInvoker& onInterface(const std::string& interfaceName); template AsyncMethodInvoker& withArguments(_Args&&... args); - //template void uponReplyInvoke(std::function callback); template void uponReplyInvoke(_Function&& callback); private: diff --git a/include/sdbus-c++/ConvenienceClasses.inl b/include/sdbus-c++/ConvenienceClasses.inl index f0f2632..137cc21 100755 --- a/include/sdbus-c++/ConvenienceClasses.inl +++ b/include/sdbus-c++/ConvenienceClasses.inl @@ -113,7 +113,7 @@ namespace sdbus { { inputSignature_ = signature_of_function_input_arguments<_Function>::str(); outputSignature_ = signature_of_function_output_arguments<_Function>::str(); - asyncCallback_ = [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodResult result) + asyncCallback_ = [callback = std::forward<_Function>(callback)](MethodCall msg, MethodResult&& result) { // Create a tuple of callback input arguments' types, which will be used // as a storage for the argument values deserialized from the message. @@ -123,7 +123,7 @@ namespace sdbus { msg >> inputArgs; // Invoke callback with input arguments from the tuple. - sdbus::apply(callback, std::move(result), inputArgs); // TODO: Use std::apply when switching to full C++17 support + sdbus::apply(callback, std::move(result), std::move(inputArgs)); // TODO: Use std::apply when switching to full C++17 support }; return *this; diff --git a/include/sdbus-c++/Error.h b/include/sdbus-c++/Error.h index 66edbf6..bc37016 100755 --- a/include/sdbus-c++/Error.h +++ b/include/sdbus-c++/Error.h @@ -70,12 +70,12 @@ namespace sdbus { sdbus::Error createError(int errNo, const std::string& customMsg); } -#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \ - throw sdbus::createError((_ERRNO), (_MSG)) \ +#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \ + throw sdbus::createError((_ERRNO), (_MSG)) \ /**/ -#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \ - if (_COND) SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \ +#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \ + if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \ /**/ #endif /* SDBUS_CXX_ERROR_H_ */ diff --git a/include/sdbus-c++/Message.h b/include/sdbus-c++/Message.h index 6d5197e..453ba16 100644 --- a/include/sdbus-c++/Message.h +++ b/include/sdbus-c++/Message.h @@ -50,10 +50,22 @@ namespace sdbus { class MethodReply; class Signal; template class Result; + + namespace internal { + class ISdBus; + } } namespace sdbus { + // Assume the caller has already obtained message ownership + struct adopt_message_t { explicit adopt_message_t() = default; }; +#ifdef __cpp_inline_variables + inline constexpr adopt_message_t adopt_message{}; +#else + constexpr adopt_message_t adopt_message{}; +#endif + /********************************************//** * @class Message * @@ -72,7 +84,9 @@ namespace sdbus { { public: Message() = default; - Message(void *msg) noexcept; + Message(internal::ISdBus* sdbus) noexcept; + 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; @@ -141,10 +155,8 @@ namespace sdbus { void rewind(bool complete); protected: - void* getMsg() const; - - private: void* msg_{}; + internal::ISdBus* sdbus_{}; mutable bool ok_{true}; }; @@ -167,6 +179,7 @@ namespace sdbus { { public: using Message::Message; + AsyncMethodCall() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration) AsyncMethodCall(MethodCall&& call) noexcept; void send(void* callback, void* userData) const; }; diff --git a/include/sdbus-c++/MethodResult.h b/include/sdbus-c++/MethodResult.h index 4a0378c..06032cc 100755 --- a/include/sdbus-c++/MethodResult.h +++ b/include/sdbus-c++/MethodResult.h @@ -1,7 +1,7 @@ /** * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * - * @file ConvenienceClasses.h + * @file MethodResult.h * * Created on: Nov 8, 2016 * Project: sdbus-c++ @@ -51,40 +51,23 @@ namespace sdbus { { protected: friend sdbus::internal::Object; + MethodResult() = default; - MethodResult(const MethodCall& msg, sdbus::internal::Object& object); + MethodResult(MethodCall msg); + + MethodResult(const MethodResult&) = delete; + MethodResult& operator=(const MethodResult&) = delete; + + MethodResult(MethodResult&& other) = default; + MethodResult& operator=(MethodResult&& other) = default; template void returnResults(const _Results&... results) const; void returnError(const Error& error) const; - private: - void send(const MethodReply& reply) const; - private: MethodCall call_; - sdbus::internal::Object* object_{}; }; - template - inline void MethodResult::returnResults(const _Results&... results) const - { - assert(call_.isValid()); - auto reply = call_.createReply(); -#ifdef __cpp_fold_expressions - (reply << ... << results); -#else - using _ = std::initializer_list; - (void)_{(void(reply << results), 0)...}; -#endif - send(reply); - } - - inline void MethodResult::returnError(const Error& error) const - { - auto reply = call_.createErrorReply(error); - send(reply); - } - /********************************************//** * @class Result * @@ -98,29 +81,14 @@ namespace sdbus { { public: Result() = default; - Result(MethodResult result); + Result(MethodResult&& result); + void returnResults(const _Results&... results) const; void returnError(const Error& error) const; }; - template - inline Result<_Results...>::Result(MethodResult result) - : MethodResult(std::move(result)) - { - } - - template - inline void Result<_Results...>::returnResults(const _Results&... results) const - { - MethodResult::returnResults(results...); - } - - template - inline void Result<_Results...>::returnError(const Error& error) const - { - MethodResult::returnError(error); - } - } +#include + #endif /* SDBUS_CXX_METHODRESULT_H_ */ diff --git a/include/sdbus-c++/MethodResult.inl b/include/sdbus-c++/MethodResult.inl new file mode 100755 index 0000000..ced38b4 --- /dev/null +++ b/include/sdbus-c++/MethodResult.inl @@ -0,0 +1,79 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file MethodResult.inl + * + * Created on: Mar 21, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_METHODRESULT_INL_ +#define SDBUS_CXX_METHODRESULT_INL_ + +#include +#include + +namespace sdbus { + + inline MethodResult::MethodResult(MethodCall msg) + : call_(std::move(msg)) + { + } + + template + inline void MethodResult::returnResults(const _Results&... results) const + { + assert(call_.isValid()); + auto reply = call_.createReply(); +#ifdef __cpp_fold_expressions + (reply << ... << results); +#else + using _ = std::initializer_list; + (void)_{(void(reply << results), 0)...}; +#endif + reply.send(); + } + + inline void MethodResult::returnError(const Error& error) const + { + auto reply = call_.createErrorReply(error); + reply.send(); + } + + template + inline Result<_Results...>::Result(MethodResult&& result) + : MethodResult(std::move(result)) + { + } + + template + inline void Result<_Results...>::returnResults(const _Results&... results) const + { + MethodResult::returnResults(results...); + } + + template + inline void Result<_Results...>::returnError(const Error& error) const + { + MethodResult::returnError(error); + } + +} + +#endif /* SDBUS_CXX_METHODRESULT_INL_ */ diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 2b37899..116130f 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -52,7 +52,7 @@ namespace sdbus { namespace sdbus { using method_callback = std::function; - using async_method_callback = std::function; + using async_method_callback = std::function; using async_reply_handler = std::function; using signal_handler = std::function; using property_set_callback = std::function; @@ -383,6 +383,13 @@ namespace sdbus { static constexpr bool is_async = true; }; + template + struct function_traits&&, _Args...)> + : public function_traits_base, _Args...> + { + static constexpr bool is_async = true; + }; + template struct function_traits<_ReturnType(*)(_Args...)> : public function_traits<_ReturnType(_Args...)> diff --git a/include/sdbus-c++/Types.h b/include/sdbus-c++/Types.h index 2f305c0..b6e755c 100644 --- a/include/sdbus-c++/Types.h +++ b/include/sdbus-c++/Types.h @@ -139,20 +139,22 @@ namespace sdbus { { public: using std::string::string; - using std::string::operator=; + ObjectPath() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration) ObjectPath(std::string path) : std::string(std::move(path)) {} + using std::string::operator=; }; class Signature : public std::string { public: using std::string::string; - using std::string::operator=; + Signature() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration) Signature(std::string path) : std::string(std::move(path)) {} + using std::string::operator=; }; } diff --git a/src/Connection.cpp b/src/Connection.cpp index c08ece0..b24264b 100755 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -36,21 +36,23 @@ namespace sdbus { namespace internal { Connection::Connection(Connection::BusType type, std::unique_ptr&& interface) - : busType_(type) - , iface_(std::move(interface)) + : iface_(std::move(interface)) + , busType_(type) { + assert(iface_ != nullptr); + auto bus = openBus(busType_); bus_.reset(bus); finishHandshake(bus); - notificationFd_ = createLoopNotificationDescriptor(); + loopExitFd_ = createProcessingLoopExitDescriptor(); } Connection::~Connection() { leaveProcessingLoop(); - closeLoopNotificationDescriptor(notificationFd_); + closeProcessingLoopExitDescriptor(loopExitFd_); } void Connection::requestName(const std::string& name) @@ -76,14 +78,13 @@ void Connection::enterProcessingLoop() auto success = waitForNextRequest(); if (!success) break; // Exit processing loop - if (success.asyncMsgsToProcess) - processAsynchronousMessages(); } } void Connection::enterProcessingLoopAsync() { - asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); }); + if (!asyncLoopThread_.joinable()) + asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); }); } void Connection::leaveProcessingLoop() @@ -92,6 +93,16 @@ void Connection::leaveProcessingLoop() joinWithProcessingLoop(); } +const ISdBus& Connection::getSdBusInterface() const +{ + return *iface_.get(); +} + +ISdBus& Connection::getSdBusInterface() +{ + return *iface_.get(); +} + sd_bus_slot* Connection::addObjectVTable( const std::string& objectPath , const std::string& interfaceName , const sd_bus_vtable* vtable @@ -116,16 +127,13 @@ void Connection::removeObjectVTable(sd_bus_slot* vtableHandle) iface_->sd_bus_slot_unref(vtableHandle); } -sdbus::MethodCall Connection::createMethodCall( const std::string& destination - , const std::string& objectPath - , const std::string& interfaceName - , const std::string& methodName ) const +MethodCall Connection::createMethodCall( const std::string& destination + , const std::string& objectPath + , const std::string& interfaceName + , const std::string& methodName ) const { sd_bus_message *sdbusMsg{}; - // Returned message will become an owner of sdbusMsg - SCOPE_EXIT{ iface_->sd_bus_message_unref(sdbusMsg); }; - auto r = iface_->sd_bus_message_new_method_call( bus_.get() , &sdbusMsg , destination.c_str() @@ -135,18 +143,15 @@ sdbus::MethodCall Connection::createMethodCall( const std::string& destination SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r); - return MethodCall(sdbusMsg); + return MethodCall{sdbusMsg, iface_.get(), adopt_message}; } -sdbus::Signal Connection::createSignal( const std::string& objectPath - , const std::string& interfaceName - , const std::string& signalName ) const +Signal Connection::createSignal( const std::string& objectPath + , const std::string& interfaceName + , const std::string& signalName ) const { sd_bus_message *sdbusSignal{}; - // Returned message will become an owner of sdbusSignal - SCOPE_EXIT{ iface_->sd_bus_message_unref(sdbusSignal); }; - auto r = iface_->sd_bus_message_new_signal( bus_.get() , &sdbusSignal , objectPath.c_str() @@ -155,7 +160,7 @@ sdbus::Signal Connection::createSignal( const std::string& objectPath SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r); - return Signal(sdbusSignal); + return Signal{sdbusSignal, iface_.get(), adopt_message}; } sd_bus_slot* Connection::registerSignalHandler( const std::string& objectPath @@ -179,20 +184,6 @@ void Connection::unregisterSignalHandler(sd_bus_slot* handlerCookie) iface_->sd_bus_slot_unref(handlerCookie); } -void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply) -{ - std::lock_guard guard(mutex_); - asyncReplies_.push(reply); - notifyProcessingLoop(); -} - -std::unique_ptr Connection::clone() const -{ - auto interface = std::make_unique(); - assert(interface != nullptr); - return std::make_unique(busType_, std::move(interface)); -} - sd_bus* Connection::openBus(Connection::BusType type) { sd_bus* bus{}; @@ -223,7 +214,7 @@ void Connection::finishHandshake(sd_bus* bus) SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r); } -int Connection::createLoopNotificationDescriptor() +int Connection::createProcessingLoopExitDescriptor() { auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK); @@ -232,26 +223,25 @@ int Connection::createLoopNotificationDescriptor() return r; } -void Connection::closeLoopNotificationDescriptor(int fd) +void Connection::closeProcessingLoopExitDescriptor(int fd) { close(fd); } -void Connection::notifyProcessingLoop() +void Connection::notifyProcessingLoopToExit() { - assert(notificationFd_ >= 0); + assert(loopExitFd_ >= 0); uint64_t value = 1; - auto r = write(notificationFd_, &value, sizeof(value)); - + auto r = write(loopExitFd_, &value, sizeof(value)); SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno); } -void Connection::notifyProcessingLoopToExit() +void Connection::clearExitNotification() { - exitLoopThread_ = true; - - notifyProcessingLoop(); + uint64_t value{}; + auto r = read(loopExitFd_, &value, sizeof(value)); + SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno); } void Connection::joinWithProcessingLoop() @@ -273,60 +263,35 @@ bool Connection::processPendingRequest() return r > 0; } -void Connection::processAsynchronousMessages() -{ - std::lock_guard guard(mutex_); - while (!asyncReplies_.empty()) - { - auto reply = asyncReplies_.front(); - asyncReplies_.pop(); - reply.send(); - } -} - -Connection::WaitResult Connection::waitForNextRequest() +bool Connection::waitForNextRequest() { auto bus = bus_.get(); assert(bus != nullptr); - assert(notificationFd_ != 0); + assert(loopExitFd_ != 0); - auto r = iface_->sd_bus_get_fd(bus); - SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r); - auto sdbusFd = r; + ISdBus::PollData sdbusPollData; + auto r = iface_->sd_bus_get_poll_data(bus, &sdbusPollData); + SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r); - r = iface_->sd_bus_get_events(bus); - SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus events", -r); - short int sdbusEvents = r; - - uint64_t usec; - iface_->sd_bus_get_timeout(bus, &usec); - - struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}}; + struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_, POLLIN, 0}}; auto fdsCount = sizeof(fds)/sizeof(fds[0]); - r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000); + auto timeout = sdbusPollData.timeout_usec == (uint64_t) -1 ? (uint64_t)-1 : (sdbusPollData.timeout_usec+999)/1000; + r = poll(fds, fdsCount, timeout); if (r < 0 && errno == EINTR) - return {true, false}; // Try again + return true; // Try again SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno); if (fds[1].revents & POLLIN) { - if (exitLoopThread_) - return {false, false}; // Got exit notification - - // Otherwise we have some async messages to process - - uint64_t value{}; - auto r = read(notificationFd_, &value, sizeof(value)); - SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno); - - return {false, true}; + clearExitNotification(); + return false; } - return {true, false}; + return true; } std::string Connection::composeSignalMatchFilter( const std::string& objectPath @@ -361,8 +326,8 @@ std::unique_ptr createSystemBusConnection() { auto interface = std::make_unique(); assert(interface != nullptr); - return std::make_unique(sdbus::internal::Connection::BusType::eSystem, - std::move(interface)); + return std::make_unique( sdbus::internal::Connection::BusType::eSystem + , std::move(interface)); } std::unique_ptr createSystemBusConnection(const std::string& name) @@ -376,8 +341,8 @@ std::unique_ptr createSessionBusConnection() { auto interface = std::make_unique(); assert(interface != nullptr); - return std::make_unique(sdbus::internal::Connection::BusType::eSession, - std::move(interface)); + return std::make_unique( sdbus::internal::Connection::BusType::eSession + , std::move(interface)); } std::unique_ptr createSessionBusConnection(const std::string& name) diff --git a/src/Connection.h b/src/Connection.h index 311e170..94d2153 100755 --- a/src/Connection.h +++ b/src/Connection.h @@ -29,14 +29,11 @@ #include #include #include "IConnection.h" +#include "ScopeGuard.h" #include "ISdBus.h" - #include #include #include -#include -#include -#include namespace sdbus { namespace internal { @@ -60,19 +57,22 @@ namespace sdbus { namespace internal { void enterProcessingLoopAsync() override; void leaveProcessingLoop() override; + const ISdBus& getSdBusInterface() const override; + ISdBus& getSdBusInterface() override; + sd_bus_slot* addObjectVTable( const std::string& objectPath , const std::string& interfaceName , const sd_bus_vtable* vtable , void* userData ) override; void removeObjectVTable(sd_bus_slot* vtableHandle) override; - sdbus::MethodCall createMethodCall( const std::string& destination - , const std::string& objectPath - , const std::string& interfaceName - , const std::string& methodName ) const override; - sdbus::Signal createSignal( const std::string& objectPath - , const std::string& interfaceName - , const std::string& signalName ) const override; + MethodCall createMethodCall( const std::string& destination + , const std::string& objectPath + , const std::string& interfaceName + , const std::string& methodName ) const override; + Signal createSignal( const std::string& objectPath + , const std::string& interfaceName + , const std::string& signalName ) const override; sd_bus_slot* registerSignalHandler( const std::string& objectPath , const std::string& interfaceName @@ -81,32 +81,18 @@ namespace sdbus { namespace internal { , void* userData ) override; void unregisterSignalHandler(sd_bus_slot* handlerCookie) override; - void sendReplyAsynchronously(const sdbus::MethodReply& reply) override; - - std::unique_ptr clone() const override; - private: - struct WaitResult - { - bool msgsToProcess; - bool asyncMsgsToProcess; - operator bool() - { - return msgsToProcess || asyncMsgsToProcess; - } - }; sd_bus* openBus(Connection::BusType type); void finishHandshake(sd_bus* bus); - static int createLoopNotificationDescriptor(); - static void closeLoopNotificationDescriptor(int fd); + static int createProcessingLoopExitDescriptor(); + static void closeProcessingLoopExitDescriptor(int fd); bool processPendingRequest(); - void processAsynchronousMessages(); - WaitResult waitForNextRequest(); + bool waitForNextRequest(); static std::string composeSignalMatchFilter( const std::string& objectPath , const std::string& interfaceName , const std::string& signalName ); - void notifyProcessingLoop(); void notifyProcessingLoopToExit(); + void clearExitNotification(); void joinWithProcessingLoop(); private: @@ -115,14 +101,10 @@ namespace sdbus { namespace internal { { return iface_->sd_bus_flush_close_unref(bus); }}; - std::thread asyncLoopThread_; - std::mutex mutex_; - std::queue asyncReplies_; - std::atomic exitLoopThread_; - int notificationFd_{-1}; BusType busType_; - static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000; + std::thread asyncLoopThread_; + int loopExitFd_{-1}; }; }} diff --git a/src/IConnection.h b/src/IConnection.h index 8296131..30fb061 100755 --- a/src/IConnection.h +++ b/src/IConnection.h @@ -33,8 +33,12 @@ // Forward declaration namespace sdbus { class MethodCall; + class AsyncMethodCall; class MethodReply; class Signal; + namespace internal { + class ISdBus; + } } namespace sdbus { @@ -43,20 +47,23 @@ namespace internal { class IConnection { public: + virtual const ISdBus& getSdBusInterface() const = 0; + virtual ISdBus& getSdBusInterface() = 0; + virtual sd_bus_slot* addObjectVTable( const std::string& objectPath , const std::string& interfaceName , const sd_bus_vtable* vtable , void* userData ) = 0; virtual void removeObjectVTable(sd_bus_slot* vtableHandle) = 0; - virtual sdbus::MethodCall createMethodCall( const std::string& destination - , const std::string& objectPath - , const std::string& interfaceName - , const std::string& methodName ) const = 0; + virtual MethodCall createMethodCall( const std::string& destination + , const std::string& objectPath + , const std::string& interfaceName + , const std::string& methodName ) const = 0; - virtual sdbus::Signal createSignal( const std::string& objectPath - , const std::string& interfaceName - , const std::string& signalName ) const = 0; + virtual Signal createSignal( const std::string& objectPath + , const std::string& interfaceName + , const std::string& signalName ) const = 0; virtual sd_bus_slot* registerSignalHandler( const std::string& objectPath , const std::string& interfaceName @@ -68,10 +75,6 @@ namespace internal { virtual void enterProcessingLoopAsync() = 0; virtual void leaveProcessingLoop() = 0; - virtual void sendReplyAsynchronously(const sdbus::MethodReply& reply) = 0; - - virtual std::unique_ptr clone() const = 0; - virtual ~IConnection() = default; }; diff --git a/src/ISdBus.h b/src/ISdBus.h index b18297f..e1b11ba 100644 --- a/src/ISdBus.h +++ b/src/ISdBus.h @@ -34,21 +34,37 @@ namespace sdbus { namespace internal { class ISdBus { public: + struct PollData + { + int fd; + short int events; + uint64_t timeout_usec; + }; + + virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) = 0; + virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0; + + virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) = 0; + virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0; + virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0; + + virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0; + virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0; + virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0; + virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) = 0; + + virtual int sd_bus_open_user(sd_bus **ret) = 0; + virtual int sd_bus_open_system(sd_bus **ret) = 0; virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0; virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0; virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) = 0; - virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0; - virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0; - virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0; - virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0; virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0; - virtual int sd_bus_open_user(sd_bus **ret) = 0; - virtual int sd_bus_open_system(sd_bus **ret) = 0; - virtual int sd_bus_flush(sd_bus *bus) = 0; + virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0; + virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0; - virtual int sd_bus_get_fd(sd_bus *bus) = 0; - virtual int sd_bus_get_events(sd_bus *bus) = 0; - virtual int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) = 0; + virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0; + + virtual int sd_bus_flush(sd_bus *bus) = 0; virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0; virtual ~ISdBus() = default; diff --git a/src/Message.cpp b/src/Message.cpp index 635756a..d02a69c 100755 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -27,17 +27,34 @@ #include #include #include "MessageUtils.h" +#include "SdBus.h" #include "ScopeGuard.h" #include #include namespace sdbus { -Message::Message(void *msg) noexcept +Message::Message(internal::ISdBus* sdbus) noexcept + : sdbus_(sdbus) +{ + assert(sdbus_ != nullptr); +} + +Message::Message(void *msg, internal::ISdBus* sdbus) noexcept : msg_(msg) + , sdbus_(sdbus) { assert(msg_ != nullptr); - sd_bus_message_ref((sd_bus_message*)msg_); + assert(sdbus_ != nullptr); + sdbus_->sd_bus_message_ref((sd_bus_message*)msg_); +} + +Message::Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept + : msg_(msg) + , sdbus_(sdbus) +{ + assert(msg_ != nullptr); + assert(sdbus_ != nullptr); } Message::Message(const Message& other) noexcept @@ -48,12 +65,13 @@ Message::Message(const Message& other) noexcept Message& Message::operator=(const Message& other) noexcept { if (msg_) - sd_bus_message_unref((sd_bus_message*)msg_); + sdbus_->sd_bus_message_unref((sd_bus_message*)msg_); msg_ = other.msg_; + sdbus_ = other.sdbus_; ok_ = other.ok_; - sd_bus_message_ref((sd_bus_message*)msg_); + sdbus_->sd_bus_message_ref((sd_bus_message*)msg_); return *this; } @@ -66,10 +84,12 @@ Message::Message(Message&& other) noexcept Message& Message::operator=(Message&& other) noexcept { if (msg_) - sd_bus_message_unref((sd_bus_message*)msg_); + sdbus_->sd_bus_message_unref((sd_bus_message*)msg_); msg_ = other.msg_; other.msg_ = nullptr; + sdbus_ = other.sdbus_; + other.sdbus_ = nullptr; ok_ = other.ok_; other.ok_ = true; @@ -79,7 +99,7 @@ Message& Message::operator=(Message&& other) noexcept Message::~Message() { if (msg_) - sd_bus_message_unref((sd_bus_message*)msg_); + sdbus_->sd_bus_message_unref((sd_bus_message*)msg_); } Message& Message::operator<<(bool item) @@ -558,7 +578,7 @@ void Message::peekType(std::string& type, std::string& contents) const bool Message::isValid() const { - return msg_ != nullptr; + return msg_ != nullptr && sdbus_ != nullptr; } bool Message::isEmpty() const @@ -566,20 +586,15 @@ bool Message::isEmpty() const return sd_bus_message_is_empty((sd_bus_message*)msg_); } -void* Message::getMsg() const -{ - return msg_; -} - void MethodCall::dontExpectReply() { - auto r = sd_bus_message_set_expect_reply((sd_bus_message*)getMsg(), 0); + auto r = sd_bus_message_set_expect_reply((sd_bus_message*)msg_, 0); SDBUS_THROW_ERROR_IF(r < 0, "Failed to set the dont-expect-reply flag", -r); } bool MethodCall::doesntExpectReply() const { - auto r = sd_bus_message_get_expect_reply((sd_bus_message*)getMsg()); + auto r = sd_bus_message_get_expect_reply((sd_bus_message*)msg_); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r); return r > 0 ? false : true; } @@ -594,41 +609,35 @@ MethodReply MethodCall::send() const MethodReply MethodCall::sendWithReply() const { - sd_bus_message* sdbusReply{}; - SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply sd_bus_error sdbusError = SD_BUS_ERROR_NULL; SCOPE_EXIT{ sd_bus_error_free(&sdbusError); }; - auto r = sd_bus_call(nullptr, (sd_bus_message*)getMsg(), 0, &sdbusError, &sdbusReply); + sd_bus_message* sdbusReply{}; + auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, 0, &sdbusError, &sdbusReply); if (sd_bus_error_is_set(&sdbusError)) - { throw sdbus::Error(sdbusError.name, sdbusError.message); - } SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r); - return MethodReply(sdbusReply); + return MethodReply{sdbusReply, sdbus_, adopt_message}; } MethodReply MethodCall::sendWithNoReply() const { - auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr); + auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r); + return MethodReply{}; // No reply } MethodReply MethodCall::createReply() const { - sd_bus_message *sdbusReply{}; - SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply - - auto r = sd_bus_message_new_method_return((sd_bus_message*)getMsg(), &sdbusReply); + sd_bus_message* sdbusReply{}; + auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r); - assert(sdbusReply != nullptr); - - return MethodReply(sdbusReply); + return MethodReply{sdbusReply, sdbus_, adopt_message}; } MethodReply MethodCall::createErrorReply(const Error& error) const @@ -637,37 +646,33 @@ MethodReply MethodCall::createErrorReply(const Error& error) const SCOPE_EXIT{ sd_bus_error_free(&sdbusError); }; sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str()); - sd_bus_message *sdbusErrorReply{}; - SCOPE_EXIT{ sd_bus_message_unref(sdbusErrorReply); }; // Returned message will become an owner of sdbusErrorReply - - auto r = sd_bus_message_new_method_error((sd_bus_message*)getMsg(), &sdbusErrorReply, &sdbusError); + sd_bus_message* sdbusErrorReply{}; + auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r); - assert(sdbusErrorReply != nullptr); - - return MethodReply(sdbusErrorReply); + return MethodReply{sdbusErrorReply, sdbus_, adopt_message}; } AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept - : Message(call) + : Message(std::move(call)) { } void AsyncMethodCall::send(void* callback, void* userData) const { - auto r = sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)getMsg(), (sd_bus_message_handler_t)callback, userData, 0); + auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, 0); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r); } void MethodReply::send() const { - auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr); + auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr); SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r); } void Signal::send() const { - auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr); + auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr); SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r); } @@ -676,16 +681,24 @@ Message createPlainMessage() int r; sd_bus* bus{}; - SCOPE_EXIT{ sd_bus_unref(bus); }; // sdbusMsg will hold reference to the bus + SCOPE_EXIT{ sd_bus_unref(bus); }; r = sd_bus_default_system(&bus); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r); + thread_local struct BusReferenceKeeper + { + BusReferenceKeeper(sd_bus* bus) : bus_(bus) {} + ~BusReferenceKeeper() { sd_bus_unref(bus_); } + sd_bus* bus_{}; + } busReferenceKeeper{bus}; + sd_bus_message* sdbusMsg{}; - SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; // Returned message will become an owner of sdbusMsg r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r); - return Message(sdbusMsg); + thread_local internal::SdBus sdbus; + + return Message{sdbusMsg, &sdbus, adopt_message}; } } diff --git a/src/MethodResult.cpp b/src/MethodResult.cpp deleted file mode 100644 index 9dd9149..0000000 --- a/src/MethodResult.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland - * - * @file Object.cpp - * - * Created on: Nov 8, 2016 - * Project: sdbus-c++ - * Description: High-level D-Bus IPC C++ library based on sd-bus - * - * This file is part of sdbus-c++. - * - * sdbus-c++ is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 of the License, or - * (at your option) any later version. - * - * sdbus-c++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with sdbus-c++. If not, see . - */ - -#include -#include "Object.h" - -namespace sdbus { - -MethodResult::MethodResult(const MethodCall& msg, sdbus::internal::Object& object) - : call_(msg) - , object_(&object) -{ -} - -void MethodResult::send(const MethodReply& reply) const -{ - assert(object_ != nullptr); - object_->sendReplyAsynchronously(reply); -} - -} diff --git a/src/Object.cpp b/src/Object.cpp index 560cc4a..949094a 100755 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -74,10 +74,10 @@ void Object::registerMethod( const std::string& interfaceName { SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL); - auto asyncCallback = [this, callback = std::move(asyncMethodCallback)](MethodCall& msg) + auto asyncCallback = [callback = std::move(asyncMethodCallback)](MethodCall& msg) { - MethodResult result{msg, *this}; - callback(msg, result); + MethodResult result{msg}; + callback(std::move(msg), std::move(result)); }; auto& interface = interfaces_[interfaceName]; @@ -156,17 +156,9 @@ sdbus::Signal Object::createSignal(const std::string& interfaceName, const std:: void Object::emitSignal(const sdbus::Signal& message) { - // TODO: Make signal emitting asynchronous. Now signal can probably be emitted only from user code - // handled within the D-Bus processing loop thread, but not from any thread. In principle it will - // be the same as async replies. message.send(); } -void Object::sendReplyAsynchronously(const MethodReply& reply) -{ - connection_.sendReplyAsynchronously(reply); -} - const std::vector& Object::createInterfaceVTable(InterfaceData& interfaceData) { auto& vtable = interfaceData.vtable_; @@ -242,9 +234,11 @@ void Object::activateInterfaceVTable( const std::string& interfaceName int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError) { - MethodCall message(sdbusMessage); - auto* object = static_cast(userData); + assert(object != nullptr); + + MethodCall message{sdbusMessage, &object->connection_.getSdBusInterface()}; + // Note: The lookup can be optimized by using sorted vectors instead of associative containers auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_; assert(callback); @@ -269,9 +263,9 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/ , void *userData , sd_bus_error *retError ) { - Message reply(sdbusReply); - auto* object = static_cast(userData); + assert(object != nullptr); + // Note: The lookup can be optimized by using sorted vectors instead of associative containers auto& callback = object->interfaces_[interface].properties_[property].getCallback_; // Getter can be empty - the case of "write-only" property @@ -281,6 +275,8 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/ return 1; } + Message reply{sdbusReply, &object->connection_.getSdBusInterface()}; + try { callback(reply); @@ -301,13 +297,15 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/ , void *userData , sd_bus_error *retError ) { - Message value(sdbusValue); - auto* object = static_cast(userData); + assert(object != nullptr); + // Note: The lookup can be optimized by using sorted vectors instead of associative containers auto& callback = object->interfaces_[interface].properties_[property].setCallback_; assert(callback); + Message value{sdbusValue, &object->connection_.getSdBusInterface()}; + try { callback(value); diff --git a/src/Object.h b/src/Object.h index 4d2e44f..32070fd 100644 --- a/src/Object.h +++ b/src/Object.h @@ -84,8 +84,6 @@ namespace internal { sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override; void emitSignal(const sdbus::Signal& message) override; - void sendReplyAsynchronously(const MethodReply& reply); - private: using InterfaceName = std::string; struct InterfaceData diff --git a/src/ObjectProxy.cpp b/src/ObjectProxy.cpp index 2aeb16e..3975a17 100755 --- a/src/ObjectProxy.cpp +++ b/src/ObjectProxy.cpp @@ -30,45 +30,40 @@ #include "IConnection.h" #include #include +#include +#include namespace sdbus { namespace internal { ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath) : connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ }) - , ownConnection_(false) , destination_(std::move(destination)) , objectPath_(std::move(objectPath)) { + // The connection is not ours only, it is managed by the client and we just reference it here, + // so we expect the client to manage the event loop upon this connection themselves. } ObjectProxy::ObjectProxy( std::unique_ptr&& connection , std::string destination , std::string objectPath ) : connection_(std::move(connection)) - , ownConnection_(true) , destination_(std::move(destination)) , objectPath_(std::move(objectPath)) { -} - -ObjectProxy::~ObjectProxy() -{ - // If the dedicated connection for signals is used, we have to stop the processing loop - // upon this connection prior to unregistering signal slots in the interfaces_ container, - // otherwise we might have a race condition of two threads working upon one connection. - if (signalConnection_ != nullptr) - signalConnection_->leaveProcessingLoop(); + // The connection is ours only, so we have to manage event loop upon this connection, + // so we get signals, async replies, and other messages from D-Bus. + connection_->enterProcessingLoopAsync(); } MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName) { - // Tell, don't ask return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName); } AsyncMethodCall ObjectProxy::createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName) { - return AsyncMethodCall{createMethodCall(interfaceName, methodName)}; + return AsyncMethodCall{ObjectProxy::createMethodCall(interfaceName, methodName)}; } MethodReply ObjectProxy::callMethod(const MethodCall& message) @@ -78,8 +73,11 @@ MethodReply ObjectProxy::callMethod(const MethodCall& message) void ObjectProxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) { - // The new-ed handler gets deleted in the sdbus_async_reply_handler - message.send((void*)&ObjectProxy::sdbus_async_reply_handler, new async_reply_handler(std::move(asyncReplyCallback))); + auto callback = (void*)&ObjectProxy::sdbus_async_reply_handler; + // Allocated userData gets deleted in the sdbus_async_reply_handler + auto userData = new AsyncReplyUserData{*this, std::move(asyncReplyCallback)}; + + message.send(callback, userData); } void ObjectProxy::registerSignalHandler( const std::string& interfaceName @@ -99,30 +97,7 @@ void ObjectProxy::registerSignalHandler( const std::string& interfaceName void ObjectProxy::finishRegistration() { - bool hasSignals = listensToSignals(); - - if (hasSignals && ownConnection_) - { - // Let's use dedicated signalConnection_ for signals, - // which will then be used by the processing loop thread. - signalConnection_ = connection_->clone(); - registerSignalHandlers(*signalConnection_); - signalConnection_->enterProcessingLoopAsync(); - } - else if (hasSignals) - { - // Let's used connection provided from the outside. - registerSignalHandlers(*connection_); - } -} - -bool ObjectProxy::listensToSignals() const -{ - for (auto& interfaceItem : interfaces_) - if (!interfaceItem.second.signals_.empty()) - return true; - - return false; + registerSignalHandlers(*connection_); } void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& connection) @@ -149,31 +124,34 @@ void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& connectio int ObjectProxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError) { - MethodReply message(sdbusMessage); - // We are assuming the ownership of the async reply handler pointer passed here - std::unique_ptr asyncReplyCallback{static_cast(userData)}; - assert(asyncReplyCallback != nullptr); + std::unique_ptr asyncReplyUserData{static_cast(userData)}; + assert(asyncReplyUserData != nullptr); + assert(asyncReplyUserData->callback); - if (!sd_bus_error_is_set(retError)) + MethodReply message{sdbusMessage, &asyncReplyUserData->proxy.connection_->getSdBusInterface()}; + + const auto* error = sd_bus_message_get_error(sdbusMessage); + if (error == nullptr) { - (*asyncReplyCallback)(message, nullptr); + asyncReplyUserData->callback(message, nullptr); } else { - sdbus::Error error(retError->name, retError->message); - (*asyncReplyCallback)(message, &error); + sdbus::Error exception(error->name, error->message); + asyncReplyUserData->callback(message, &exception); } } int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) { - Signal message(sdbusMessage); + auto* proxy = static_cast(userData); + assert(proxy != nullptr); + + Signal message{sdbusMessage, &proxy->connection_->getSdBusInterface()}; - auto* object = static_cast(userData); - assert(object != nullptr); // Note: The lookup can be optimized by using sorted vectors instead of associative containers - auto& callback = object->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_; + auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_; assert(callback); callback(message); diff --git a/src/ObjectProxy.h b/src/ObjectProxy.h index 8ce510b..14b1665 100755 --- a/src/ObjectProxy.h +++ b/src/ObjectProxy.h @@ -50,7 +50,6 @@ namespace internal { ObjectProxy( std::unique_ptr&& connection , std::string destination , std::string objectPath ); - ~ObjectProxy() override; MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override; AsyncMethodCall createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName) override; @@ -63,7 +62,12 @@ namespace internal { void finishRegistration() override; private: - bool listensToSignals() const; + struct AsyncReplyUserData + { + ObjectProxy& proxy; + async_reply_handler callback; + }; + 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_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError); @@ -72,8 +76,6 @@ namespace internal { std::unique_ptr< sdbus::internal::IConnection , std::function > connection_; - bool ownConnection_{}; - std::unique_ptr signalConnection_; std::string destination_; std::string objectPath_; diff --git a/src/SdBus.cpp b/src/SdBus.cpp index 5b5cd70..310e77c 100644 --- a/src/SdBus.cpp +++ b/src/SdBus.cpp @@ -28,44 +28,67 @@ namespace sdbus { namespace internal { -int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) +sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m) { - return ::sd_bus_request_name(bus, name, flags); -} + std::unique_lock lock(sdbusMutex_); -int SdBus::sd_bus_release_name(sd_bus *bus, const char *name) -{ - return ::sd_bus_release_name(bus, name); -} - -int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) -{ - return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata); -} - -sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot) -{ - return ::sd_bus_slot_unref(slot); -} - -int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) -{ - return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member); + return ::sd_bus_message_ref(m); } sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m) { + std::unique_lock lock(sdbusMutex_); + return ::sd_bus_message_unref(m); } +int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_send(bus, m, cookie); +} + +int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_call(bus, m, usec, ret_error, reply); +} + +int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec); +} + +int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member); +} + int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) { + std::unique_lock lock(sdbusMutex_); + return ::sd_bus_message_new_signal(bus, m, path, interface, member); } -int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) +int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) { - return :: sd_bus_add_match(bus, slot, match, callback, userdata); + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_message_new_method_return(call, m); +} + +int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_message_new_method_error(call, m, e); } int SdBus::sd_bus_open_user(sd_bus **ret) @@ -78,29 +101,70 @@ int SdBus::sd_bus_open_system(sd_bus **ret) return ::sd_bus_open_system(ret); } -int SdBus::sd_bus_flush(sd_bus *bus) +int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { - return ::sd_bus_flush(bus); + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_request_name(bus, name, flags); +} + +int SdBus::sd_bus_release_name(sd_bus *bus, const char *name) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_release_name(bus, name); +} + +int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata); +} + +int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) +{ + std::unique_lock lock(sdbusMutex_); + + return :: sd_bus_add_match(bus, slot, match, callback, userdata); +} + +sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot) +{ + std::unique_lock lock(sdbusMutex_); + + return ::sd_bus_slot_unref(slot); } int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r) { + std::unique_lock lock(sdbusMutex_); + return ::sd_bus_process(bus, r); } -int SdBus::sd_bus_get_fd(sd_bus *bus) +int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data) { - return ::sd_bus_get_fd(bus); + std::unique_lock lock(sdbusMutex_); + + auto r = ::sd_bus_get_fd(bus); + if (r < 0) + return r; + data->fd = r; + + r = ::sd_bus_get_events(bus); + if (r < 0) + return r; + data->events = static_cast(r); + + r = ::sd_bus_get_timeout(bus, &data->timeout_usec); + + return r; } -int SdBus::sd_bus_get_events(sd_bus *bus) +int SdBus::sd_bus_flush(sd_bus *bus) { - return ::sd_bus_get_events(bus); -} - -int SdBus::sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) -{ - return ::sd_bus_get_timeout(bus, timeout_usec); + return ::sd_bus_flush(bus); } sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus) diff --git a/src/SdBus.h b/src/SdBus.h index d9e45d6..c6f5c2f 100644 --- a/src/SdBus.h +++ b/src/SdBus.h @@ -28,28 +28,41 @@ #define SDBUS_CXX_SDBUS_H #include "ISdBus.h" +#include namespace sdbus { namespace internal { -class SdBus : public ISdBus +class SdBus final : public ISdBus { public: - int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override; - int sd_bus_release_name(sd_bus *bus, const char *name) override; - int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override; - sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override; - int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override; - sd_bus_message* sd_bus_message_unref(sd_bus_message *m) override; - int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override; - int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override; - int sd_bus_open_user(sd_bus **ret) override; - int sd_bus_open_system(sd_bus **ret) override; - int sd_bus_flush(sd_bus *bus) override; - int sd_bus_process(sd_bus *bus, sd_bus_message **r) override; - int sd_bus_get_fd(sd_bus *bus) override; - int sd_bus_get_events(sd_bus *bus) override; - int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) override; - sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override; + virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) override; + virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) override; + + virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) override; + virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override; + virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override; + + virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override; + virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override; + virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override; + virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override; + + virtual int sd_bus_open_user(sd_bus **ret) override; + virtual int sd_bus_open_system(sd_bus **ret) override; + virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override; + virtual int sd_bus_release_name(sd_bus *bus, const char *name) override; + virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override; + virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override; + virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override; + + virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override; + virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override; + + virtual int sd_bus_flush(sd_bus *bus) override; + virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override; + +private: + std::recursive_mutex sdbusMutex_; }; }} diff --git a/src/Types.cpp b/src/Types.cpp index 51672ed..2af41ea 100644 --- a/src/Types.cpp +++ b/src/Types.cpp @@ -29,7 +29,7 @@ #include #include -namespace sdbus { /*namespace internal {*/ +namespace sdbus { Variant::Variant() : msg_(createPlainMessage()) diff --git a/stub-generator/AdaptorGenerator.cpp b/stub-generator/AdaptorGenerator.cpp index 50b5eaf..056caa5 100644 --- a/stub-generator/AdaptorGenerator.cpp +++ b/stub-generator/AdaptorGenerator.cpp @@ -201,7 +201,7 @@ std::tuple AdaptorGenerator::processMethods(const Node Nodes outArgs = args.select("direction" , "out"); std::string argStr, argTypeStr; - std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs); + std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs, async); using namespace std::string_literals; @@ -210,7 +210,7 @@ std::tuple AdaptorGenerator::processMethods(const Node << ".onInterface(interfaceName)" << ".implementedAs(" << "[this](" - << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "") + << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "") << argTypeStr << "){ " << (async ? "" : "return ") << "this->" << methodName << "(" << (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "") @@ -222,7 +222,7 @@ std::tuple AdaptorGenerator::processMethods(const Node << (async ? "void" : outArgsToType(outArgs)) << " " << methodName << "(" - << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "") + << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "") << argTypeStr << ") = 0;" << endl; } diff --git a/stub-generator/BaseGenerator.cpp b/stub-generator/BaseGenerator.cpp index 619ef01..532546d 100644 --- a/stub-generator/BaseGenerator.cpp +++ b/stub-generator/BaseGenerator.cpp @@ -104,7 +104,7 @@ std::tuple BaseGenerator::generateNamespaces(const std::s } -std::tuple BaseGenerator::argsToNamesAndTypes(const Nodes& args) const +std::tuple BaseGenerator::argsToNamesAndTypes(const Nodes& args, bool async) const { std::ostringstream argSS, argTypeSS, typeSS; @@ -124,8 +124,16 @@ std::tuple BaseGenerator::argsToNamesAndT argName = "arg" + std::to_string(i); } auto type = signature_to_type(arg->get("type")); - argSS << argName; - argTypeSS << "const " << type << "& " << argName; + if (!async) + { + argSS << argName; + argTypeSS << "const " << type << "& " << argName; + } + else + { + argSS << "std::move(" << argName << ")"; + argTypeSS << type << " " << argName; + } typeSS << type; } diff --git a/stub-generator/BaseGenerator.h b/stub-generator/BaseGenerator.h index ce482e9..1268068 100644 --- a/stub-generator/BaseGenerator.h +++ b/stub-generator/BaseGenerator.h @@ -87,7 +87,7 @@ protected: * @param args * @return tuple: argument names, argument types and names, argument types */ - std::tuple argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args) const; + std::tuple argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args, bool async = false) const; /** * Output arguments to return type diff --git a/test/integrationtests/AdaptorAndProxy_test.cpp b/test/integrationtests/AdaptorAndProxy_test.cpp index 5c9318c..3b5229b 100644 --- a/test/integrationtests/AdaptorAndProxy_test.cpp +++ b/test/integrationtests/AdaptorAndProxy_test.cpp @@ -90,7 +90,6 @@ public: }; std::unique_ptr AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection(); - } /*-------------------------------------*/ diff --git a/test/integrationtests/TestingAdaptor.h b/test/integrationtests/TestingAdaptor.h index 5f09feb..d328321 100644 --- a/test/integrationtests/TestingAdaptor.h +++ b/test/integrationtests/TestingAdaptor.h @@ -45,7 +45,7 @@ public: protected: - void noArgNoReturn() const { } + void noArgNoReturn() const {} int32_t getInt() const { return INT32_VALUE; } diff --git a/test/perftests/client.cpp b/test/perftests/client.cpp index 61b6a02..c0dd945 100644 --- a/test/perftests/client.cpp +++ b/test/perftests/client.cpp @@ -48,11 +48,11 @@ protected: { static unsigned int counter = 0; static std::chrono::time_point startTime; - + assert(data.size() == m_msgSize); - + ++counter; - + if (counter == 1) startTime = std::chrono::steady_clock::now(); else if (counter == m_msgCount) @@ -62,7 +62,7 @@ protected: counter = 0; } } - + public: unsigned int m_msgSize{}; unsigned int m_msgCount{}; @@ -95,68 +95,68 @@ int main(int /*argc*/, char */*argv*/[]) const unsigned int repetitions{20}; unsigned int msgCount = 1000; unsigned int msgSize{}; - + msgSize = 20; std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; client.m_msgCount = msgCount; client.m_msgSize = msgSize; for (unsigned int r = 0; r < repetitions; ++r) { client.sendDataSignals(msgCount, msgSize); - + std::this_thread::sleep_for(1000ms); } - + msgSize = 1000; std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; client.m_msgCount = msgCount; client.m_msgSize = msgSize; for (unsigned int r = 0; r < repetitions; ++r) { client.sendDataSignals(msgCount, msgSize); - + std::this_thread::sleep_for(1000ms); } - + msgSize = 20; std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; for (unsigned int r = 0; r < repetitions; ++r) { auto str1 = createRandomString(msgSize/2); auto str2 = createRandomString(msgSize/2); - + auto startTime = std::chrono::steady_clock::now(); for (unsigned int i = 0; i < msgCount; i++) { auto result = client.concatenateTwoStrings(str1, str2); - + assert(result.size() == str1.size() + str2.size()); assert(result.size() == msgSize); } auto stopTime = std::chrono::steady_clock::now(); std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast(stopTime - startTime).count() << " ms" << std::endl; - + std::this_thread::sleep_for(1000ms); } - + msgSize = 1000; std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; for (unsigned int r = 0; r < repetitions; ++r) { auto str1 = createRandomString(msgSize/2); auto str2 = createRandomString(msgSize/2); - + auto startTime = std::chrono::steady_clock::now(); for (unsigned int i = 0; i < msgCount; i++) { auto result = client.concatenateTwoStrings(str1, str2); - + assert(result.size() == str1.size() + str2.size()); assert(result.size() == msgSize); } auto stopTime = std::chrono::steady_clock::now(); std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast(stopTime - startTime).count() << " ms" << std::endl; - + std::this_thread::sleep_for(1000ms); } - + return 0; } diff --git a/test/unittests/mocks/SdBusMock.h b/test/unittests/mocks/SdBusMock.h index 22fc13b..91c72f4 100644 --- a/test/unittests/mocks/SdBusMock.h +++ b/test/unittests/mocks/SdBusMock.h @@ -34,21 +34,30 @@ class SdBusMock : public sdbus::internal::ISdBus { public: + MOCK_METHOD1(sd_bus_message_ref, sd_bus_message*(sd_bus_message *m)); + MOCK_METHOD1(sd_bus_message_unref, sd_bus_message*(sd_bus_message *m)); + + MOCK_METHOD3(sd_bus_send, int(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)); + MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)); + MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)); + + MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)); + MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)); + MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m)); + MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)); + + MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret)); + MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret)); MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags)); MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name)); MOCK_METHOD6(sd_bus_add_object_vtable, int(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata)); - MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot)); - MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)); - MOCK_METHOD1(sd_bus_message_unref, sd_bus_message* (sd_bus_message *m)); - MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)); MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)); - MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret)); - MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret)); - MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus)); + MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot)); + MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r)); - MOCK_METHOD1(sd_bus_get_fd, int(sd_bus *bus)); - MOCK_METHOD1(sd_bus_get_events, int(sd_bus *bus)); - MOCK_METHOD2(sd_bus_get_timeout, int(sd_bus *bus, uint64_t *timeout_usec)); + MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data)); + + MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus)); MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus)); };