Re-design sdbus-c++ approach to connections (#47)

Fixes #33 , among others
This commit is contained in:
Stanislav Angelovič
2019-03-25 16:28:31 +01:00
committed by GitHub
parent 26c6ea8730
commit fd7be39dd4
29 changed files with 526 additions and 452 deletions

View File

@ -28,7 +28,6 @@ set(SDBUSCPP_CPP_SRCS
${SDBUSCPP_SOURCE_DIR}/ConvenienceClasses.cpp ${SDBUSCPP_SOURCE_DIR}/ConvenienceClasses.cpp
${SDBUSCPP_SOURCE_DIR}/Error.cpp ${SDBUSCPP_SOURCE_DIR}/Error.cpp
${SDBUSCPP_SOURCE_DIR}/Message.cpp ${SDBUSCPP_SOURCE_DIR}/Message.cpp
${SDBUSCPP_SOURCE_DIR}/MethodResult.cpp
${SDBUSCPP_SOURCE_DIR}/Object.cpp ${SDBUSCPP_SOURCE_DIR}/Object.cpp
${SDBUSCPP_SOURCE_DIR}/ObjectProxy.cpp ${SDBUSCPP_SOURCE_DIR}/ObjectProxy.cpp
${SDBUSCPP_SOURCE_DIR}/Types.cpp ${SDBUSCPP_SOURCE_DIR}/Types.cpp
@ -58,10 +57,11 @@ set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h ${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h
${SDBUSCPP_INCLUDE_DIR}/Message.h ${SDBUSCPP_INCLUDE_DIR}/Message.h
${SDBUSCPP_INCLUDE_DIR}/MethodResult.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}/Types.h
${SDBUSCPP_INCLUDE_DIR}/TypeTraits.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}) set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HDRS})
@ -94,6 +94,7 @@ set_target_properties(sdbus-c++
VERSION "${SDBUSCPP_VERSION}" VERSION "${SDBUSCPP_VERSION}"
SOVERSION "${SDBUSCPP_VERSION_MAJOR}" SOVERSION "${SDBUSCPP_VERSION_MAJOR}"
OUTPUT_NAME "sdbus-c++") OUTPUT_NAME "sdbus-c++")
target_link_libraries(sdbus-c++ ${SYSTEMD_LIBRARIES}) target_link_libraries(sdbus-c++ ${SYSTEMD_LIBRARIES})
#---------------------------------- #----------------------------------

View File

@ -183,7 +183,6 @@ namespace sdbus {
AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName); AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName); AsyncMethodInvoker& onInterface(const std::string& interfaceName);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args); template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
//template <typename... _OutputArgs> void uponReplyInvoke(std::function<void(const Error*, _OutputArgs...)> callback);
template <typename _Function> void uponReplyInvoke(_Function&& callback); template <typename _Function> void uponReplyInvoke(_Function&& callback);
private: private:

View File

@ -113,7 +113,7 @@ namespace sdbus {
{ {
inputSignature_ = signature_of_function_input_arguments<_Function>::str(); inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_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 // Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message. // as a storage for the argument values deserialized from the message.
@ -123,7 +123,7 @@ namespace sdbus {
msg >> inputArgs; msg >> inputArgs;
// Invoke callback with input arguments from the tuple. // 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; return *this;

View File

@ -70,12 +70,12 @@ namespace sdbus {
sdbus::Error createError(int errNo, const std::string& customMsg); sdbus::Error createError(int errNo, const std::string& customMsg);
} }
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \ #define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
throw sdbus::createError((_ERRNO), (_MSG)) \ throw sdbus::createError((_ERRNO), (_MSG)) \
/**/ /**/
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \ #define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
if (_COND) SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \ if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
/**/ /**/
#endif /* SDBUS_CXX_ERROR_H_ */ #endif /* SDBUS_CXX_ERROR_H_ */

View File

@ -50,10 +50,22 @@ namespace sdbus {
class MethodReply; class MethodReply;
class Signal; class Signal;
template <typename... _Results> class Result; template <typename... _Results> class Result;
namespace internal {
class ISdBus;
}
} }
namespace sdbus { 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 * @class Message
* *
@ -72,7 +84,9 @@ namespace sdbus {
{ {
public: public:
Message() = default; 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(const Message&) noexcept;
Message& operator=(const Message&) noexcept; Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept; Message(Message&& other) noexcept;
@ -141,10 +155,8 @@ namespace sdbus {
void rewind(bool complete); void rewind(bool complete);
protected: protected:
void* getMsg() const;
private:
void* msg_{}; void* msg_{};
internal::ISdBus* sdbus_{};
mutable bool ok_{true}; mutable bool ok_{true};
}; };
@ -167,6 +179,7 @@ namespace sdbus {
{ {
public: public:
using Message::Message; using Message::Message;
AsyncMethodCall() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
AsyncMethodCall(MethodCall&& call) noexcept; AsyncMethodCall(MethodCall&& call) noexcept;
void send(void* callback, void* userData) const; void send(void* callback, void* userData) const;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* *
* @file ConvenienceClasses.h * @file MethodResult.h
* *
* Created on: Nov 8, 2016 * Created on: Nov 8, 2016
* Project: sdbus-c++ * Project: sdbus-c++
@ -51,40 +51,23 @@ namespace sdbus {
{ {
protected: protected:
friend sdbus::internal::Object; friend sdbus::internal::Object;
MethodResult() = default; 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 <typename... _Results> void returnResults(const _Results&... results) const; template <typename... _Results> void returnResults(const _Results&... results) const;
void returnError(const Error& error) const; void returnError(const Error& error) const;
private:
void send(const MethodReply& reply) const;
private: private:
MethodCall call_; MethodCall call_;
sdbus::internal::Object* object_{};
}; };
template <typename... _Results>
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<int>;
(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 * @class Result
* *
@ -98,29 +81,14 @@ namespace sdbus {
{ {
public: public:
Result() = default; Result() = default;
Result(MethodResult result); Result(MethodResult&& result);
void returnResults(const _Results&... results) const; void returnResults(const _Results&... results) const;
void returnError(const Error& error) const; void returnError(const Error& error) const;
}; };
template <typename... _Results>
inline Result<_Results...>::Result(MethodResult result)
: MethodResult(std::move(result))
{
}
template <typename... _Results>
inline void Result<_Results...>::returnResults(const _Results&... results) const
{
MethodResult::returnResults(results...);
}
template <typename... _Results>
inline void Result<_Results...>::returnError(const Error& error) const
{
MethodResult::returnError(error);
}
} }
#include <sdbus-c++/MethodResult.inl>
#endif /* SDBUS_CXX_METHODRESULT_H_ */ #endif /* SDBUS_CXX_METHODRESULT_H_ */

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_METHODRESULT_INL_
#define SDBUS_CXX_METHODRESULT_INL_
#include <sdbus-c++/MethodResult.h>
#include <cassert>
namespace sdbus {
inline MethodResult::MethodResult(MethodCall msg)
: call_(std::move(msg))
{
}
template <typename... _Results>
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<int>;
(void)_{(void(reply << results), 0)...};
#endif
reply.send();
}
inline void MethodResult::returnError(const Error& error) const
{
auto reply = call_.createErrorReply(error);
reply.send();
}
template <typename... _Results>
inline Result<_Results...>::Result(MethodResult&& result)
: MethodResult(std::move(result))
{
}
template <typename... _Results>
inline void Result<_Results...>::returnResults(const _Results&... results) const
{
MethodResult::returnResults(results...);
}
template <typename... _Results>
inline void Result<_Results...>::returnError(const Error& error) const
{
MethodResult::returnError(error);
}
}
#endif /* SDBUS_CXX_METHODRESULT_INL_ */

View File

@ -52,7 +52,7 @@ namespace sdbus {
namespace sdbus { namespace sdbus {
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>; using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
using async_method_callback = std::function<void(MethodCall& msg, MethodResult result)>; using async_method_callback = std::function<void(MethodCall msg, MethodResult&& result)>;
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 property_set_callback = std::function<void(Message& msg)>; using property_set_callback = std::function<void(Message& msg)>;
@ -383,6 +383,13 @@ namespace sdbus {
static constexpr bool is_async = true; static constexpr bool is_async = true;
}; };
template <typename... _Args, typename... _Results>
struct function_traits<void(Result<_Results...>&&, _Args...)>
: public function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
};
template <typename _ReturnType, typename... _Args> template <typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(*)(_Args...)> struct function_traits<_ReturnType(*)(_Args...)>
: public function_traits<_ReturnType(_Args...)> : public function_traits<_ReturnType(_Args...)>

View File

@ -139,20 +139,22 @@ namespace sdbus {
{ {
public: public:
using std::string::string; 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) ObjectPath(std::string path)
: std::string(std::move(path)) : std::string(std::move(path))
{} {}
using std::string::operator=;
}; };
class Signature : public std::string class Signature : public std::string
{ {
public: public:
using std::string::string; 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) Signature(std::string path)
: std::string(std::move(path)) : std::string(std::move(path))
{} {}
using std::string::operator=;
}; };
} }

View File

@ -36,21 +36,23 @@
namespace sdbus { namespace internal { namespace sdbus { namespace internal {
Connection::Connection(Connection::BusType type, std::unique_ptr<ISdBus>&& interface) Connection::Connection(Connection::BusType type, std::unique_ptr<ISdBus>&& interface)
: busType_(type) : iface_(std::move(interface))
, iface_(std::move(interface)) , busType_(type)
{ {
assert(iface_ != nullptr);
auto bus = openBus(busType_); auto bus = openBus(busType_);
bus_.reset(bus); bus_.reset(bus);
finishHandshake(bus); finishHandshake(bus);
notificationFd_ = createLoopNotificationDescriptor(); loopExitFd_ = createProcessingLoopExitDescriptor();
} }
Connection::~Connection() Connection::~Connection()
{ {
leaveProcessingLoop(); leaveProcessingLoop();
closeLoopNotificationDescriptor(notificationFd_); closeProcessingLoopExitDescriptor(loopExitFd_);
} }
void Connection::requestName(const std::string& name) void Connection::requestName(const std::string& name)
@ -76,14 +78,13 @@ void Connection::enterProcessingLoop()
auto success = waitForNextRequest(); auto success = waitForNextRequest();
if (!success) if (!success)
break; // Exit processing loop break; // Exit processing loop
if (success.asyncMsgsToProcess)
processAsynchronousMessages();
} }
} }
void Connection::enterProcessingLoopAsync() void Connection::enterProcessingLoopAsync()
{ {
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); }); if (!asyncLoopThread_.joinable())
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); });
} }
void Connection::leaveProcessingLoop() void Connection::leaveProcessingLoop()
@ -92,6 +93,16 @@ void Connection::leaveProcessingLoop()
joinWithProcessingLoop(); joinWithProcessingLoop();
} }
const ISdBus& Connection::getSdBusInterface() const
{
return *iface_.get();
}
ISdBus& Connection::getSdBusInterface()
{
return *iface_.get();
}
sd_bus_slot* Connection::addObjectVTable( const std::string& objectPath sd_bus_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
@ -116,16 +127,13 @@ void Connection::removeObjectVTable(sd_bus_slot* vtableHandle)
iface_->sd_bus_slot_unref(vtableHandle); iface_->sd_bus_slot_unref(vtableHandle);
} }
sdbus::MethodCall Connection::createMethodCall( const std::string& destination MethodCall Connection::createMethodCall( const std::string& destination
, const std::string& objectPath , const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& methodName ) const , const std::string& methodName ) const
{ {
sd_bus_message *sdbusMsg{}; 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() auto r = iface_->sd_bus_message_new_method_call( bus_.get()
, &sdbusMsg , &sdbusMsg
, destination.c_str() , 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); 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 Signal Connection::createSignal( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& signalName ) const , const std::string& signalName ) const
{ {
sd_bus_message *sdbusSignal{}; 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() auto r = iface_->sd_bus_message_new_signal( bus_.get()
, &sdbusSignal , &sdbusSignal
, objectPath.c_str() , 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); 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 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); iface_->sd_bus_slot_unref(handlerCookie);
} }
void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply)
{
std::lock_guard<std::mutex> guard(mutex_);
asyncReplies_.push(reply);
notifyProcessingLoop();
}
std::unique_ptr<sdbus::internal::IConnection> Connection::clone() const
{
auto interface = std::make_unique<SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>(busType_, std::move(interface));
}
sd_bus* Connection::openBus(Connection::BusType type) sd_bus* Connection::openBus(Connection::BusType type)
{ {
sd_bus* bus{}; 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); 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); auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
@ -232,26 +223,25 @@ int Connection::createLoopNotificationDescriptor()
return r; return r;
} }
void Connection::closeLoopNotificationDescriptor(int fd) void Connection::closeProcessingLoopExitDescriptor(int fd)
{ {
close(fd); close(fd);
} }
void Connection::notifyProcessingLoop() void Connection::notifyProcessingLoopToExit()
{ {
assert(notificationFd_ >= 0); assert(loopExitFd_ >= 0);
uint64_t value = 1; 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
} }
void Connection::notifyProcessingLoopToExit() void Connection::clearExitNotification()
{ {
exitLoopThread_ = true; uint64_t value{};
auto r = read(loopExitFd_, &value, sizeof(value));
notifyProcessingLoop(); SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
} }
void Connection::joinWithProcessingLoop() void Connection::joinWithProcessingLoop()
@ -273,60 +263,35 @@ bool Connection::processPendingRequest()
return r > 0; return r > 0;
} }
void Connection::processAsynchronousMessages() bool Connection::waitForNextRequest()
{
std::lock_guard<std::mutex> guard(mutex_);
while (!asyncReplies_.empty())
{
auto reply = asyncReplies_.front();
asyncReplies_.pop();
reply.send();
}
}
Connection::WaitResult Connection::waitForNextRequest()
{ {
auto bus = bus_.get(); auto bus = bus_.get();
assert(bus != nullptr); assert(bus != nullptr);
assert(notificationFd_ != 0); assert(loopExitFd_ != 0);
auto r = iface_->sd_bus_get_fd(bus); ISdBus::PollData sdbusPollData;
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r); auto r = iface_->sd_bus_get_poll_data(bus, &sdbusPollData);
auto sdbusFd = r; SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
r = iface_->sd_bus_get_events(bus); struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_, POLLIN, 0}};
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}};
auto fdsCount = sizeof(fds)/sizeof(fds[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) 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
if (fds[1].revents & POLLIN) if (fds[1].revents & POLLIN)
{ {
if (exitLoopThread_) clearExitNotification();
return {false, false}; // Got exit notification return false;
// 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};
} }
return {true, false}; return true;
} }
std::string Connection::composeSignalMatchFilter( const std::string& objectPath std::string Connection::composeSignalMatchFilter( const std::string& objectPath
@ -361,8 +326,8 @@ std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
{ {
auto interface = std::make_unique<sdbus::internal::SdBus>(); auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr); assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSystem, return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSystem
std::move(interface)); , std::move(interface));
} }
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name) std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
@ -376,8 +341,8 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
{ {
auto interface = std::make_unique<sdbus::internal::SdBus>(); auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr); assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSession, return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSession
std::move(interface)); , std::move(interface));
} }
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name) std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)

View File

@ -29,14 +29,11 @@
#include <sdbus-c++/IConnection.h> #include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h> #include <sdbus-c++/Message.h>
#include "IConnection.h" #include "IConnection.h"
#include "ScopeGuard.h"
#include "ISdBus.h" #include "ISdBus.h"
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <atomic>
#include <mutex>
#include <queue>
namespace sdbus { namespace internal { namespace sdbus { namespace internal {
@ -60,19 +57,22 @@ namespace sdbus { namespace internal {
void enterProcessingLoopAsync() override; void enterProcessingLoopAsync() override;
void leaveProcessingLoop() override; void leaveProcessingLoop() override;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
sd_bus_slot* addObjectVTable( const std::string& objectPath sd_bus_slot* addObjectVTable( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const sd_bus_vtable* vtable , const sd_bus_vtable* vtable
, void* userData ) override; , void* userData ) override;
void removeObjectVTable(sd_bus_slot* vtableHandle) override; void removeObjectVTable(sd_bus_slot* vtableHandle) override;
sdbus::MethodCall createMethodCall( const std::string& destination MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath , const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& methodName ) const override; , const std::string& methodName ) const override;
sdbus::Signal createSignal( const std::string& objectPath Signal createSignal( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& signalName ) const override; , const std::string& signalName ) const override;
sd_bus_slot* registerSignalHandler( const std::string& objectPath sd_bus_slot* registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
@ -81,32 +81,18 @@ namespace sdbus { namespace internal {
, void* userData ) override; , void* userData ) override;
void unregisterSignalHandler(sd_bus_slot* handlerCookie) override; void unregisterSignalHandler(sd_bus_slot* handlerCookie) override;
void sendReplyAsynchronously(const sdbus::MethodReply& reply) override;
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
private: private:
struct WaitResult
{
bool msgsToProcess;
bool asyncMsgsToProcess;
operator bool()
{
return msgsToProcess || asyncMsgsToProcess;
}
};
sd_bus* openBus(Connection::BusType type); sd_bus* openBus(Connection::BusType type);
void finishHandshake(sd_bus* bus); void finishHandshake(sd_bus* bus);
static int createLoopNotificationDescriptor(); static int createProcessingLoopExitDescriptor();
static void closeLoopNotificationDescriptor(int fd); static void closeProcessingLoopExitDescriptor(int fd);
bool processPendingRequest(); bool processPendingRequest();
void processAsynchronousMessages(); bool waitForNextRequest();
WaitResult waitForNextRequest();
static std::string composeSignalMatchFilter( const std::string& objectPath static std::string composeSignalMatchFilter( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& signalName ); , const std::string& signalName );
void notifyProcessingLoop();
void notifyProcessingLoopToExit(); void notifyProcessingLoopToExit();
void clearExitNotification();
void joinWithProcessingLoop(); void joinWithProcessingLoop();
private: private:
@ -115,14 +101,10 @@ namespace sdbus { namespace internal {
{ {
return iface_->sd_bus_flush_close_unref(bus); return iface_->sd_bus_flush_close_unref(bus);
}}; }};
std::thread asyncLoopThread_;
std::mutex mutex_;
std::queue<MethodReply> asyncReplies_;
std::atomic<bool> exitLoopThread_;
int notificationFd_{-1};
BusType busType_; BusType busType_;
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000; std::thread asyncLoopThread_;
int loopExitFd_{-1};
}; };
}} }}

View File

@ -33,8 +33,12 @@
// Forward declaration // Forward declaration
namespace sdbus { namespace sdbus {
class MethodCall; class MethodCall;
class AsyncMethodCall;
class MethodReply; class MethodReply;
class Signal; class Signal;
namespace internal {
class ISdBus;
}
} }
namespace sdbus { namespace sdbus {
@ -43,20 +47,23 @@ namespace internal {
class IConnection class IConnection
{ {
public: public:
virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0;
virtual sd_bus_slot* addObjectVTable( const std::string& objectPath virtual sd_bus_slot* addObjectVTable( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const sd_bus_vtable* vtable , const sd_bus_vtable* vtable
, void* userData ) = 0; , void* userData ) = 0;
virtual void removeObjectVTable(sd_bus_slot* vtableHandle) = 0; virtual void removeObjectVTable(sd_bus_slot* vtableHandle) = 0;
virtual sdbus::MethodCall createMethodCall( const std::string& destination virtual MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath , const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& methodName ) const = 0; , const std::string& methodName ) const = 0;
virtual sdbus::Signal createSignal( const std::string& objectPath virtual Signal createSignal( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
, const std::string& signalName ) const = 0; , const std::string& signalName ) const = 0;
virtual sd_bus_slot* registerSignalHandler( const std::string& objectPath virtual sd_bus_slot* registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName , const std::string& interfaceName
@ -68,10 +75,6 @@ namespace internal {
virtual void enterProcessingLoopAsync() = 0; virtual void enterProcessingLoopAsync() = 0;
virtual void leaveProcessingLoop() = 0; virtual void leaveProcessingLoop() = 0;
virtual void sendReplyAsynchronously(const sdbus::MethodReply& reply) = 0;
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
virtual ~IConnection() = default; virtual ~IConnection() = default;
}; };

View File

@ -34,21 +34,37 @@ namespace sdbus { namespace internal {
class ISdBus class ISdBus
{ {
public: 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_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_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 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_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 sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
virtual int sd_bus_open_system(sd_bus **ret) = 0;
virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 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_poll_data(sd_bus *bus, PollData* data) = 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_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0; virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual ~ISdBus() = default; virtual ~ISdBus() = default;

View File

@ -27,17 +27,34 @@
#include <sdbus-c++/Types.h> #include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h> #include <sdbus-c++/Error.h>
#include "MessageUtils.h" #include "MessageUtils.h"
#include "SdBus.h"
#include "ScopeGuard.h" #include "ScopeGuard.h"
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <cassert> #include <cassert>
namespace sdbus { 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) : msg_(msg)
, sdbus_(sdbus)
{ {
assert(msg_ != nullptr); 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 Message::Message(const Message& other) noexcept
@ -48,12 +65,13 @@ Message::Message(const Message& other) noexcept
Message& Message::operator=(const Message& other) noexcept Message& Message::operator=(const Message& other) noexcept
{ {
if (msg_) if (msg_)
sd_bus_message_unref((sd_bus_message*)msg_); sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
msg_ = other.msg_; msg_ = other.msg_;
sdbus_ = other.sdbus_;
ok_ = other.ok_; ok_ = other.ok_;
sd_bus_message_ref((sd_bus_message*)msg_); sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
return *this; return *this;
} }
@ -66,10 +84,12 @@ Message::Message(Message&& other) noexcept
Message& Message::operator=(Message&& other) noexcept Message& Message::operator=(Message&& other) noexcept
{ {
if (msg_) if (msg_)
sd_bus_message_unref((sd_bus_message*)msg_); sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
msg_ = other.msg_; msg_ = other.msg_;
other.msg_ = nullptr; other.msg_ = nullptr;
sdbus_ = other.sdbus_;
other.sdbus_ = nullptr;
ok_ = other.ok_; ok_ = other.ok_;
other.ok_ = true; other.ok_ = true;
@ -79,7 +99,7 @@ Message& Message::operator=(Message&& other) noexcept
Message::~Message() Message::~Message()
{ {
if (msg_) if (msg_)
sd_bus_message_unref((sd_bus_message*)msg_); sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
} }
Message& Message::operator<<(bool item) Message& Message::operator<<(bool item)
@ -558,7 +578,7 @@ void Message::peekType(std::string& type, std::string& contents) const
bool Message::isValid() const bool Message::isValid() const
{ {
return msg_ != nullptr; return msg_ != nullptr && sdbus_ != nullptr;
} }
bool Message::isEmpty() const bool Message::isEmpty() const
@ -566,20 +586,15 @@ bool Message::isEmpty() const
return sd_bus_message_is_empty((sd_bus_message*)msg_); return sd_bus_message_is_empty((sd_bus_message*)msg_);
} }
void* Message::getMsg() const
{
return msg_;
}
void MethodCall::dontExpectReply() 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to set the dont-expect-reply flag", -r);
} }
bool MethodCall::doesntExpectReply() const 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r);
return r > 0 ? false : true; return r > 0 ? false : true;
} }
@ -594,41 +609,35 @@ MethodReply MethodCall::send() const
MethodReply MethodCall::sendWithReply() 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; sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); }; 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)) if (sd_bus_error_is_set(&sdbusError))
{
throw sdbus::Error(sdbusError.name, sdbusError.message); throw sdbus::Error(sdbusError.name, sdbusError.message);
}
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
return MethodReply(sdbusReply); return MethodReply{sdbusReply, sdbus_, adopt_message};
} }
MethodReply MethodCall::sendWithNoReply() const 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
return MethodReply{}; // No reply return MethodReply{}; // No reply
} }
MethodReply MethodCall::createReply() const MethodReply MethodCall::createReply() const
{ {
sd_bus_message *sdbusReply{}; sd_bus_message* sdbusReply{};
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
auto r = sd_bus_message_new_method_return((sd_bus_message*)getMsg(), &sdbusReply);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
assert(sdbusReply != nullptr); return MethodReply{sdbusReply, sdbus_, adopt_message};
return MethodReply(sdbusReply);
} }
MethodReply MethodCall::createErrorReply(const Error& error) const 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); }; SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str()); sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
sd_bus_message *sdbusErrorReply{}; sd_bus_message* sdbusErrorReply{};
SCOPE_EXIT{ sd_bus_message_unref(sdbusErrorReply); }; // Returned message will become an owner of sdbusErrorReply auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError);
auto r = sd_bus_message_new_method_error((sd_bus_message*)getMsg(), &sdbusErrorReply, &sdbusError);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
assert(sdbusErrorReply != nullptr); return MethodReply{sdbusErrorReply, sdbus_, adopt_message};
return MethodReply(sdbusErrorReply);
} }
AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept
: Message(call) : Message(std::move(call))
{ {
} }
void AsyncMethodCall::send(void* callback, void* userData) const 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
} }
void MethodReply::send() const 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
} }
void Signal::send() const 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); SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
} }
@ -676,16 +681,24 @@ Message createPlainMessage()
int r; int r;
sd_bus* bus{}; 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); r = sd_bus_default_system(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r); 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{}; 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); 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); 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};
} }
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/MethodResult.h>
#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);
}
}

View File

@ -74,10 +74,10 @@ void Object::registerMethod( const std::string& interfaceName
{ {
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL); 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}; MethodResult result{msg};
callback(msg, result); callback(std::move(msg), std::move(result));
}; };
auto& interface = interfaces_[interfaceName]; 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) 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(); message.send();
} }
void Object::sendReplyAsynchronously(const MethodReply& reply)
{
connection_.sendReplyAsynchronously(reply);
}
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData) const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{ {
auto& vtable = interfaceData.vtable_; 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) int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{ {
MethodCall message(sdbusMessage);
auto* object = static_cast<Object*>(userData); auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
MethodCall message{sdbusMessage, &object->connection_.getSdBusInterface()};
// Note: The lookup can be optimized by using sorted vectors instead of associative containers // Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_; auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
assert(callback); assert(callback);
@ -269,9 +263,9 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
Message reply(sdbusReply);
auto* object = static_cast<Object*>(userData); auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers // Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].getCallback_; auto& callback = object->interfaces_[interface].properties_[property].getCallback_;
// Getter can be empty - the case of "write-only" property // 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; return 1;
} }
Message reply{sdbusReply, &object->connection_.getSdBusInterface()};
try try
{ {
callback(reply); callback(reply);
@ -301,13 +297,15 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
Message value(sdbusValue);
auto* object = static_cast<Object*>(userData); auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers // Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].setCallback_; auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
assert(callback); assert(callback);
Message value{sdbusValue, &object->connection_.getSdBusInterface()};
try try
{ {
callback(value); callback(value);

View File

@ -84,8 +84,6 @@ namespace internal {
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override; sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
void emitSignal(const sdbus::Signal& message) override; void emitSignal(const sdbus::Signal& message) override;
void sendReplyAsynchronously(const MethodReply& reply);
private: private:
using InterfaceName = std::string; using InterfaceName = std::string;
struct InterfaceData struct InterfaceData

View File

@ -30,45 +30,40 @@
#include "IConnection.h" #include "IConnection.h"
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <cassert> #include <cassert>
#include <chrono>
#include <thread>
namespace sdbus { namespace internal { namespace sdbus { namespace internal {
ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath) ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ }) : connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
, ownConnection_(false)
, destination_(std::move(destination)) , destination_(std::move(destination))
, objectPath_(std::move(objectPath)) , 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<sdbus::internal::IConnection>&& connection ObjectProxy::ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination , std::string destination
, std::string objectPath ) , std::string objectPath )
: connection_(std::move(connection)) : connection_(std::move(connection))
, ownConnection_(true)
, destination_(std::move(destination)) , destination_(std::move(destination))
, objectPath_(std::move(objectPath)) , objectPath_(std::move(objectPath))
{ {
} // 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.
ObjectProxy::~ObjectProxy() connection_->enterProcessingLoopAsync();
{
// 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();
} }
MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName) MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
{ {
// Tell, don't ask
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName); return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
} }
AsyncMethodCall ObjectProxy::createAsyncMethodCall(const std::string& interfaceName, const std::string& 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) 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) void ObjectProxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback)
{ {
// The new-ed handler gets deleted in the sdbus_async_reply_handler auto callback = (void*)&ObjectProxy::sdbus_async_reply_handler;
message.send((void*)&ObjectProxy::sdbus_async_reply_handler, new async_reply_handler(std::move(asyncReplyCallback))); // 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 void ObjectProxy::registerSignalHandler( const std::string& interfaceName
@ -99,30 +97,7 @@ void ObjectProxy::registerSignalHandler( const std::string& interfaceName
void ObjectProxy::finishRegistration() void ObjectProxy::finishRegistration()
{ {
bool hasSignals = listensToSignals(); registerSignalHandlers(*connection_);
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;
} }
void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& 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) 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 // We are assuming the ownership of the async reply handler pointer passed here
std::unique_ptr<async_reply_handler> asyncReplyCallback{static_cast<async_reply_handler*>(userData)}; std::unique_ptr<AsyncReplyUserData> asyncReplyUserData{static_cast<AsyncReplyUserData*>(userData)};
assert(asyncReplyCallback != nullptr); 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 else
{ {
sdbus::Error error(retError->name, retError->message); sdbus::Error exception(error->name, error->message);
(*asyncReplyCallback)(message, &error); asyncReplyUserData->callback(message, &exception);
} }
} }
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/) int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{ {
Signal message(sdbusMessage); auto* proxy = static_cast<ObjectProxy*>(userData);
assert(proxy != nullptr);
Signal message{sdbusMessage, &proxy->connection_->getSdBusInterface()};
auto* object = static_cast<ObjectProxy*>(userData);
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers // 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); assert(callback);
callback(message); callback(message);

View File

@ -50,7 +50,6 @@ namespace internal {
ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination , std::string destination
, std::string objectPath ); , std::string objectPath );
~ObjectProxy() override;
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override; MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
AsyncMethodCall createAsyncMethodCall(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; void finishRegistration() override;
private: private:
bool listensToSignals() const; struct AsyncReplyUserData
{
ObjectProxy& proxy;
async_reply_handler callback;
};
void registerSignalHandlers(sdbus::internal::IConnection& connection); 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_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); 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::unique_ptr< sdbus::internal::IConnection
, std::function<void(sdbus::internal::IConnection*)> , std::function<void(sdbus::internal::IConnection*)>
> connection_; > connection_;
bool ownConnection_{};
std::unique_ptr<sdbus::internal::IConnection> signalConnection_;
std::string destination_; std::string destination_;
std::string objectPath_; std::string objectPath_;

View File

@ -28,44 +28,67 @@
namespace sdbus { namespace internal { 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<std::recursive_mutex> lock(sdbusMutex_);
}
int SdBus::sd_bus_release_name(sd_bus *bus, const char *name) return ::sd_bus_message_ref(m);
{
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);
} }
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m) sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
{ {
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_message_unref(m); return ::sd_bus_message_unref(m);
} }
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
{
std::unique_lock<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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) 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<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_message_new_signal(bus, m, path, interface, member); 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<std::recursive_mutex> 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<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_message_new_method_error(call, m, e);
} }
int SdBus::sd_bus_open_user(sd_bus **ret) 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); 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_slot_unref(slot);
} }
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r) int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
{ {
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_process(bus, r); 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<std::recursive_mutex> 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<short int>(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); return ::sd_bus_flush(bus);
}
int SdBus::sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec)
{
return ::sd_bus_get_timeout(bus, timeout_usec);
} }
sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus) sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)

View File

@ -28,28 +28,41 @@
#define SDBUS_CXX_SDBUS_H #define SDBUS_CXX_SDBUS_H
#include "ISdBus.h" #include "ISdBus.h"
#include <mutex>
namespace sdbus { namespace internal { namespace sdbus { namespace internal {
class SdBus : public ISdBus class SdBus final : public ISdBus
{ {
public: public:
int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override; virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) override;
int sd_bus_release_name(sd_bus *bus, const char *name) override; virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) 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; virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) 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; 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;
sd_bus_message* sd_bus_message_unref(sd_bus_message *m) 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;
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; 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;
int sd_bus_open_user(sd_bus **ret) 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;
int sd_bus_open_system(sd_bus **ret) override; virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
int sd_bus_flush(sd_bus *bus) override; virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override;
int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
int sd_bus_get_fd(sd_bus *bus) override; virtual int sd_bus_open_user(sd_bus **ret) override;
int sd_bus_get_events(sd_bus *bus) override; virtual int sd_bus_open_system(sd_bus **ret) override;
int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) override; virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
sd_bus *sd_bus_flush_close_unref(sd_bus *bus) 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_;
}; };
}} }}

View File

@ -29,7 +29,7 @@
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include <cassert> #include <cassert>
namespace sdbus { /*namespace internal {*/ namespace sdbus {
Variant::Variant() Variant::Variant()
: msg_(createPlainMessage()) : msg_(createPlainMessage())

View File

@ -201,7 +201,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
Nodes outArgs = args.select("direction" , "out"); Nodes outArgs = args.select("direction" , "out");
std::string argStr, argTypeStr; 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; using namespace std::string_literals;
@ -210,7 +210,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< ".onInterface(interfaceName)" << ".onInterface(interfaceName)"
<< ".implementedAs(" << ".implementedAs("
<< "[this](" << "[this]("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "") << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
<< argTypeStr << argTypeStr
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "(" << "){ " << (async ? "" : "return ") << "this->" << methodName << "("
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "") << (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
@ -222,7 +222,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< (async ? "void" : outArgsToType(outArgs)) << (async ? "void" : outArgsToType(outArgs))
<< " " << methodName << " " << methodName
<< "(" << "("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "") << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
<< argTypeStr << argTypeStr
<< ") = 0;" << endl; << ") = 0;" << endl;
} }

View File

@ -104,7 +104,7 @@ std::tuple<unsigned, std::string> BaseGenerator::generateNamespaces(const std::s
} }
std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args) const std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args, bool async) const
{ {
std::ostringstream argSS, argTypeSS, typeSS; std::ostringstream argSS, argTypeSS, typeSS;
@ -124,8 +124,16 @@ std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndT
argName = "arg" + std::to_string(i); argName = "arg" + std::to_string(i);
} }
auto type = signature_to_type(arg->get("type")); auto type = signature_to_type(arg->get("type"));
argSS << argName; if (!async)
argTypeSS << "const " << type << "& " << argName; {
argSS << argName;
argTypeSS << "const " << type << "& " << argName;
}
else
{
argSS << "std::move(" << argName << ")";
argTypeSS << type << " " << argName;
}
typeSS << type; typeSS << type;
} }

View File

@ -87,7 +87,7 @@ protected:
* @param args * @param args
* @return tuple: argument names, argument types and names, argument types * @return tuple: argument names, argument types and names, argument types
*/ */
std::tuple<std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args) const; std::tuple<std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args, bool async = false) const;
/** /**
* Output arguments to return type * Output arguments to return type

View File

@ -90,7 +90,6 @@ public:
}; };
std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection(); std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection();
} }
/*-------------------------------------*/ /*-------------------------------------*/

View File

@ -45,7 +45,7 @@ public:
protected: protected:
void noArgNoReturn() const { } void noArgNoReturn() const {}
int32_t getInt() const { return INT32_VALUE; } int32_t getInt() const { return INT32_VALUE; }

View File

@ -48,11 +48,11 @@ protected:
{ {
static unsigned int counter = 0; static unsigned int counter = 0;
static std::chrono::time_point<std::chrono::steady_clock> startTime; static std::chrono::time_point<std::chrono::steady_clock> startTime;
assert(data.size() == m_msgSize); assert(data.size() == m_msgSize);
++counter; ++counter;
if (counter == 1) if (counter == 1)
startTime = std::chrono::steady_clock::now(); startTime = std::chrono::steady_clock::now();
else if (counter == m_msgCount) else if (counter == m_msgCount)
@ -62,7 +62,7 @@ protected:
counter = 0; counter = 0;
} }
} }
public: public:
unsigned int m_msgSize{}; unsigned int m_msgSize{};
unsigned int m_msgCount{}; unsigned int m_msgCount{};
@ -95,68 +95,68 @@ int main(int /*argc*/, char */*argv*/[])
const unsigned int repetitions{20}; const unsigned int repetitions{20};
unsigned int msgCount = 1000; unsigned int msgCount = 1000;
unsigned int msgSize{}; unsigned int msgSize{};
msgSize = 20; msgSize = 20;
std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
client.m_msgCount = msgCount; client.m_msgSize = msgSize; client.m_msgCount = msgCount; client.m_msgSize = msgSize;
for (unsigned int r = 0; r < repetitions; ++r) for (unsigned int r = 0; r < repetitions; ++r)
{ {
client.sendDataSignals(msgCount, msgSize); client.sendDataSignals(msgCount, msgSize);
std::this_thread::sleep_for(1000ms); std::this_thread::sleep_for(1000ms);
} }
msgSize = 1000; msgSize = 1000;
std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
client.m_msgCount = msgCount; client.m_msgSize = msgSize; client.m_msgCount = msgCount; client.m_msgSize = msgSize;
for (unsigned int r = 0; r < repetitions; ++r) for (unsigned int r = 0; r < repetitions; ++r)
{ {
client.sendDataSignals(msgCount, msgSize); client.sendDataSignals(msgCount, msgSize);
std::this_thread::sleep_for(1000ms); std::this_thread::sleep_for(1000ms);
} }
msgSize = 20; msgSize = 20;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; 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) for (unsigned int r = 0; r < repetitions; ++r)
{ {
auto str1 = createRandomString(msgSize/2); auto str1 = createRandomString(msgSize/2);
auto str2 = createRandomString(msgSize/2); auto str2 = createRandomString(msgSize/2);
auto startTime = std::chrono::steady_clock::now(); auto startTime = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < msgCount; i++) for (unsigned int i = 0; i < msgCount; i++)
{ {
auto result = client.concatenateTwoStrings(str1, str2); auto result = client.concatenateTwoStrings(str1, str2);
assert(result.size() == str1.size() + str2.size()); assert(result.size() == str1.size() + str2.size());
assert(result.size() == msgSize); assert(result.size() == msgSize);
} }
auto stopTime = std::chrono::steady_clock::now(); auto stopTime = std::chrono::steady_clock::now();
std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl; std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
std::this_thread::sleep_for(1000ms); std::this_thread::sleep_for(1000ms);
} }
msgSize = 1000; msgSize = 1000;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl; 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) for (unsigned int r = 0; r < repetitions; ++r)
{ {
auto str1 = createRandomString(msgSize/2); auto str1 = createRandomString(msgSize/2);
auto str2 = createRandomString(msgSize/2); auto str2 = createRandomString(msgSize/2);
auto startTime = std::chrono::steady_clock::now(); auto startTime = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < msgCount; i++) for (unsigned int i = 0; i < msgCount; i++)
{ {
auto result = client.concatenateTwoStrings(str1, str2); auto result = client.concatenateTwoStrings(str1, str2);
assert(result.size() == str1.size() + str2.size()); assert(result.size() == str1.size() + str2.size());
assert(result.size() == msgSize); assert(result.size() == msgSize);
} }
auto stopTime = std::chrono::steady_clock::now(); auto stopTime = std::chrono::steady_clock::now();
std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl; std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
std::this_thread::sleep_for(1000ms); std::this_thread::sleep_for(1000ms);
} }
return 0; return 0;
} }

View File

@ -34,21 +34,30 @@
class SdBusMock : public sdbus::internal::ISdBus class SdBusMock : public sdbus::internal::ISdBus
{ {
public: 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_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_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_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_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_slot_unref, sd_bus_slot*(sd_bus_slot *slot));
MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus));
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r)); MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r));
MOCK_METHOD1(sd_bus_get_fd, int(sd_bus *bus)); MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data));
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_METHOD1(sd_bus_flush, int(sd_bus *bus));
MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus)); MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus));
}; };