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}/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})
#----------------------------------

View File

@ -183,7 +183,6 @@ namespace sdbus {
AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
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);
private:

View File

@ -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;

View File

@ -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_ */

View File

@ -50,10 +50,22 @@ namespace sdbus {
class MethodReply;
class Signal;
template <typename... _Results> 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;
};

View File

@ -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 <typename... _Results> 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 <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
*
@ -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 <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_ */

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 {
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 signal_handler = std::function<void(Signal& signal)>;
using property_set_callback = std::function<void(Message& msg)>;
@ -383,6 +383,13 @@ namespace sdbus {
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>
struct function_traits<_ReturnType(*)(_Args...)>
: public function_traits<_ReturnType(_Args...)>

View File

@ -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=;
};
}

View File

@ -36,21 +36,23 @@
namespace sdbus { namespace internal {
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_);
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<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* 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<std::mutex> 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<sdbus::IConnection> createSystemBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSystem,
std::move(interface));
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSystem
, std::move(interface));
}
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>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSession,
std::move(interface));
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSession
, std::move(interface));
}
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)

View File

@ -29,14 +29,11 @@
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include "IConnection.h"
#include "ScopeGuard.h"
#include "ISdBus.h"
#include <systemd/sd-bus.h>
#include <memory>
#include <thread>
#include <atomic>
#include <mutex>
#include <queue>
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<sdbus::internal::IConnection> 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<MethodReply> asyncReplies_;
std::atomic<bool> exitLoopThread_;
int notificationFd_{-1};
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
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<sdbus::internal::IConnection> clone() const = 0;
virtual ~IConnection() = default;
};

View File

@ -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;

View File

@ -27,17 +27,34 @@
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include "SdBus.h"
#include "ScopeGuard.h"
#include <systemd/sd-bus.h>
#include <cassert>
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};
}
}

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);
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<sd_bus_vtable>& 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<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
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<Object*>(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<Object*>(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);

View File

@ -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

View File

@ -30,45 +30,40 @@
#include "IConnection.h"
#include <systemd/sd-bus.h>
#include <cassert>
#include <chrono>
#include <thread>
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<sdbus::internal::IConnection>&& 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<async_reply_handler> asyncReplyCallback{static_cast<async_reply_handler*>(userData)};
assert(asyncReplyCallback != nullptr);
std::unique_ptr<AsyncReplyUserData> asyncReplyUserData{static_cast<AsyncReplyUserData*>(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<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
auto& callback = object->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
assert(callback);
callback(message);

View File

@ -50,7 +50,6 @@ namespace internal {
ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& 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<void(sdbus::internal::IConnection*)>
> connection_;
bool ownConnection_{};
std::unique_ptr<sdbus::internal::IConnection> signalConnection_;
std::string destination_;
std::string objectPath_;

View File

@ -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<std::recursive_mutex> 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<std::recursive_mutex> 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<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)
{
std::unique_lock<std::recursive_mutex> 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<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)
@ -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<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)
{
std::unique_lock<std::recursive_mutex> 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<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);
}
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)

View File

@ -28,28 +28,41 @@
#define SDBUS_CXX_SDBUS_H
#include "ISdBus.h"
#include <mutex>
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_;
};
}}

View File

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

View File

@ -201,7 +201,7 @@ std::tuple<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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;
}

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;
@ -124,8 +124,16 @@ std::tuple<std::string, std::string, std::string> 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;
}

View File

@ -87,7 +87,7 @@ protected:
* @param args
* @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

View File

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

View File

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

View File

@ -48,11 +48,11 @@ protected:
{
static unsigned int counter = 0;
static std::chrono::time_point<std::chrono::steady_clock> 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<std::chrono::milliseconds>(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<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
std::this_thread::sleep_for(1000ms);
}
return 0;
}

View File

@ -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));
};