forked from Kistler-Group/sdbus-cpp
Introduce support for asynchronous server-side methods (#12)
* Add preliminary changes for async server methods * Refactor the Message concept and break it into distinctive types * Continue working on async server methods (high-level API mainly) * Continue developing support for async server * Finishing async server methods * Finishing async server methods (fixing tests & cleaning up) * A little code cleaning * Add unit tests for type traits of free functions * Support for generating async server methods in stub headers * Update ChangeLog for v0.3.0 * Update the tutorial with how to use async server-side methods * Update the TOC in sdbus-c++ tutorial * Update numbering in TOC * Remove unnecessary code * Final cleanups
This commit is contained in:
committed by
Lukáš Ďurfina
parent
b041f76bfc
commit
d8fd053714
+62
-27
@@ -42,13 +42,13 @@ Connection::Connection(Connection::BusType type)
|
||||
|
||||
finishHandshake(bus);
|
||||
|
||||
exitLoopFd_ = createProcessingExitDescriptor();
|
||||
notificationFd_ = createLoopNotificationDescriptor();
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
leaveProcessingLoop();
|
||||
closeProcessingExitDescriptor(exitLoopFd_);
|
||||
closeLoopNotificationDescriptor(notificationFd_);
|
||||
}
|
||||
|
||||
void Connection::requestName(const std::string& name)
|
||||
@@ -67,13 +67,15 @@ void Connection::enterProcessingLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
auto processed = processPendingRequest(bus_.get());
|
||||
auto processed = processPendingRequest();
|
||||
if (processed)
|
||||
continue; // Process next one
|
||||
|
||||
auto success = waitForNextRequest(bus_.get(), exitLoopFd_);
|
||||
auto success = waitForNextRequest();
|
||||
if (!success)
|
||||
break; // Exit processing loop
|
||||
if (success.asyncMsgsToProcess)
|
||||
processAsynchronousMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +114,10 @@ void Connection::removeObjectVTable(void* vtableHandle)
|
||||
sd_bus_slot_unref((sd_bus_slot *)vtableHandle);
|
||||
}
|
||||
|
||||
sdbus::Message Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
sdbus::MethodCall Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
{
|
||||
sd_bus_message *sdbusMsg{};
|
||||
|
||||
@@ -131,12 +133,12 @@ sdbus::Message Connection::createMethodCall( const std::string& destination
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
||||
|
||||
return Message(sdbusMsg, Message::Type::eMethodCall);
|
||||
return MethodCall(sdbusMsg);
|
||||
}
|
||||
|
||||
sdbus::Message Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
sdbus::Signal Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
{
|
||||
sd_bus_message *sdbusSignal{};
|
||||
|
||||
@@ -151,7 +153,7 @@ sdbus::Message Connection::createSignal( const std::string& objectPath
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
||||
|
||||
return Message(sdbusSignal, Message::Type::eSignal);
|
||||
return Signal(sdbusSignal);
|
||||
}
|
||||
|
||||
void* Connection::registerSignalHandler( const std::string& objectPath
|
||||
@@ -175,6 +177,13 @@ void Connection::unregisterSignalHandler(void* handlerCookie)
|
||||
sd_bus_slot_unref((sd_bus_slot *)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
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(busType_);
|
||||
@@ -211,10 +220,8 @@ void Connection::finishHandshake(sd_bus* bus)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
||||
}
|
||||
|
||||
int Connection::createProcessingExitDescriptor()
|
||||
int Connection::createLoopNotificationDescriptor()
|
||||
{
|
||||
// Mechanism for graceful termination of processing loop
|
||||
|
||||
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||
@@ -222,16 +229,23 @@ int Connection::createProcessingExitDescriptor()
|
||||
return r;
|
||||
}
|
||||
|
||||
void Connection::closeProcessingExitDescriptor(int fd)
|
||||
void Connection::closeLoopNotificationDescriptor(int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoop()
|
||||
{
|
||||
assert(notificationFd_ >= 0);
|
||||
uint64_t value = 1;
|
||||
write(notificationFd_, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoopToExit()
|
||||
{
|
||||
assert(exitLoopFd_ >= 0);
|
||||
uint64_t value = 1;
|
||||
write(exitLoopFd_, &value, sizeof(value));
|
||||
exitLoopThread_ = true;
|
||||
|
||||
notifyProcessingLoop();
|
||||
}
|
||||
|
||||
void Connection::joinWithProcessingLoop()
|
||||
@@ -240,8 +254,10 @@ void Connection::joinWithProcessingLoop()
|
||||
asyncLoopThread_.join();
|
||||
}
|
||||
|
||||
bool Connection::processPendingRequest(sd_bus* bus)
|
||||
bool Connection::processPendingRequest()
|
||||
{
|
||||
auto bus = bus_.get();
|
||||
|
||||
assert(bus != nullptr);
|
||||
|
||||
int r = sd_bus_process(bus, nullptr);
|
||||
@@ -251,10 +267,23 @@ bool Connection::processPendingRequest(sd_bus* bus)
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
bool Connection::waitForNextRequest(sd_bus* bus, int exitFd)
|
||||
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()
|
||||
{
|
||||
auto bus = bus_.get();
|
||||
|
||||
assert(bus != nullptr);
|
||||
assert(exitFd != 0);
|
||||
assert(notificationFd_ != 0);
|
||||
|
||||
auto r = sd_bus_get_fd(bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||
@@ -267,20 +296,26 @@ bool Connection::waitForNextRequest(sd_bus* bus, int exitFd)
|
||||
uint64_t usec;
|
||||
sd_bus_get_timeout(bus, &usec);
|
||||
|
||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {exitFd, POLLIN, 0}};
|
||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}};
|
||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
|
||||
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
||||
|
||||
if (r < 0 && errno == EINTR)
|
||||
return true; // Try again
|
||||
return {true, false}; // Try again
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
||||
|
||||
if (fds[1].revents & POLLIN)
|
||||
return false; // Got exit notification
|
||||
{
|
||||
if (exitLoopThread_)
|
||||
return {false, false}; // Got exit notification
|
||||
|
||||
return true;
|
||||
// Otherwise we have some async messages to process
|
||||
return {false, true};
|
||||
}
|
||||
|
||||
return {true, false};
|
||||
}
|
||||
|
||||
std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
||||
|
||||
+33
-15
@@ -27,12 +27,14 @@
|
||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "IConnection.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
@@ -62,13 +64,13 @@ namespace sdbus { namespace internal {
|
||||
, void* userData ) override;
|
||||
void removeObjectVTable(void* vtableHandle) override;
|
||||
|
||||
sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const 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;
|
||||
|
||||
void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
@@ -77,25 +79,41 @@ namespace sdbus { namespace internal {
|
||||
, void* userData ) override;
|
||||
void unregisterSignalHandler(void* 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;
|
||||
}
|
||||
};
|
||||
static sd_bus* openBus(Connection::BusType type);
|
||||
static void finishHandshake(sd_bus* bus);
|
||||
static int createProcessingExitDescriptor();
|
||||
static void closeProcessingExitDescriptor(int fd);
|
||||
static bool processPendingRequest(sd_bus* bus);
|
||||
static bool waitForNextRequest(sd_bus* bus, int exitFd);
|
||||
static int createLoopNotificationDescriptor();
|
||||
static void closeLoopNotificationDescriptor(int fd);
|
||||
bool processPendingRequest();
|
||||
void processAsynchronousMessages();
|
||||
WaitResult waitForNextRequest();
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
void notifyProcessingLoop();
|
||||
void notifyProcessingLoopToExit();
|
||||
void joinWithProcessingLoop();
|
||||
|
||||
private:
|
||||
std::unique_ptr<sd_bus, decltype(&sd_bus_flush_close_unref)> bus_{nullptr, &sd_bus_flush_close_unref};
|
||||
std::thread asyncLoopThread_;
|
||||
std::atomic<int> exitLoopFd_{-1};
|
||||
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;
|
||||
|
||||
+12
-8
@@ -32,7 +32,9 @@
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@@ -47,14 +49,14 @@ namespace internal {
|
||||
, void* userData ) = 0;
|
||||
virtual void removeObjectVTable(void* vtableHandle) = 0;
|
||||
|
||||
virtual sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
virtual sdbus::MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
|
||||
virtual sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
virtual sdbus::Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
|
||||
virtual void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
@@ -66,6 +68,8 @@ 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;
|
||||
|
||||
@@ -5,6 +5,7 @@ libsdbus_c___la_SOURCES = \
|
||||
Connection.cpp \
|
||||
ConvenienceClasses.cpp \
|
||||
Message.cpp \
|
||||
MethodResult.cpp \
|
||||
Object.cpp \
|
||||
ObjectProxy.cpp \
|
||||
Types.cpp \
|
||||
|
||||
+67
-63
@@ -31,11 +31,10 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
namespace sdbus {
|
||||
|
||||
Message::Message(void *msg, Type type) noexcept
|
||||
Message::Message(void *msg) noexcept
|
||||
: msg_(msg)
|
||||
, type_(type)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
@@ -52,7 +51,6 @@ Message& Message::operator=(const Message& other) noexcept
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
type_ = other.type_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
@@ -72,8 +70,6 @@ Message& Message::operator=(Message&& other) noexcept
|
||||
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
type_ = other.type_;
|
||||
other.type_ = {};
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
@@ -540,59 +536,6 @@ void Message::rewind(bool complete)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
||||
}
|
||||
|
||||
Message Message::send() const
|
||||
{
|
||||
if (type_ == Type::eMethodCall)
|
||||
{
|
||||
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*)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 Message(sdbusReply);
|
||||
}
|
||||
else if (type_ == Type::eMethodReply)
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
||||
|
||||
return Message();
|
||||
}
|
||||
else if (type_ == Type::eSignal)
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||
|
||||
return Message();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return Message();
|
||||
}
|
||||
}
|
||||
|
||||
Message Message::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*)msg_, &sdbusReply);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||
|
||||
assert(sdbusReply != nullptr);
|
||||
|
||||
return Message(sdbusReply, Type::eMethodReply);
|
||||
}
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
{
|
||||
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
@@ -623,9 +566,70 @@ bool Message::isEmpty() const
|
||||
return sd_bus_message_is_empty((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Type Message::getType() const
|
||||
void* Message::getMsg() const
|
||||
{
|
||||
return type_;
|
||||
return msg_;
|
||||
}
|
||||
|
||||
MethodReply MethodCall::send() 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||
|
||||
assert(sdbusReply != nullptr);
|
||||
|
||||
return MethodReply(sdbusReply);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
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);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
|
||||
|
||||
assert(sdbusErrorReply != nullptr);
|
||||
|
||||
return MethodReply(sdbusErrorReply);
|
||||
}
|
||||
|
||||
void MethodReply::send() const
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), 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);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||
}
|
||||
|
||||
Message createPlainMessage()
|
||||
@@ -642,7 +646,7 @@ Message createPlainMessage()
|
||||
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, Message::Type::ePlainMessage);
|
||||
return Message(sdbusMsg);
|
||||
}
|
||||
|
||||
/*}*/}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* (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);
|
||||
}
|
||||
|
||||
}
|
||||
+44
-15
@@ -27,6 +27,7 @@
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include "IConnection.h"
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
@@ -48,9 +49,36 @@ void Object::registerMethod( const std::string& interfaceName
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
auto syncCallback = [callback = std::move(methodCallback)](MethodCall& msg)
|
||||
{
|
||||
auto reply = msg.createReply();
|
||||
callback(msg, reply);
|
||||
reply.send();
|
||||
};
|
||||
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback)};
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(syncCallback)};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto asyncCallback = [this, callback = std::move(asyncMethodCallback)](MethodCall& msg)
|
||||
{
|
||||
MethodResult result{msg, *this};
|
||||
callback(msg, result);
|
||||
};
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(asyncCallback)};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
@@ -108,17 +136,24 @@ void Object::finishRegistration()
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Message Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||
{
|
||||
// Tell, don't ask
|
||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||
}
|
||||
|
||||
void Object::emitSignal(const sdbus::Message& 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();
|
||||
}
|
||||
|
||||
void Object::sendReplyAsynchronously(const MethodReply& reply)
|
||||
{
|
||||
connection_.sendReplyAsynchronously(reply);
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
@@ -190,27 +225,22 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
|
||||
|
||||
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eMethodCall);
|
||||
MethodCall message(sdbusMessage);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// 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);
|
||||
|
||||
auto reply = message.createReply();
|
||||
|
||||
try
|
||||
{
|
||||
callback(message, reply);
|
||||
callback(message);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
reply.send();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -222,7 +252,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message reply(sdbusReply, Message::Type::ePlainMessage);
|
||||
Message reply(sdbusReply);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
@@ -254,7 +284,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message value(sdbusValue, Message::Type::ePlainMessage);
|
||||
Message value(sdbusValue);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
@@ -268,7 +298,6 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
+12
-3
@@ -32,6 +32,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
@@ -50,6 +51,12 @@ namespace internal {
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback ) override;
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback ) override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) override;
|
||||
@@ -67,8 +74,10 @@ namespace internal {
|
||||
|
||||
void finishRegistration() override;
|
||||
|
||||
sdbus::Message createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Message& message) override;
|
||||
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;
|
||||
@@ -79,7 +88,7 @@ namespace internal {
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
method_callback callback_;
|
||||
std::function<void(MethodCall&)> callback_;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
using SignalName = std::string;
|
||||
|
||||
+3
-3
@@ -60,13 +60,13 @@ ObjectProxy::~ObjectProxy()
|
||||
signalConnection_->leaveProcessingLoop();
|
||||
}
|
||||
|
||||
Message 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);
|
||||
}
|
||||
|
||||
Message ObjectProxy::callMethod(const Message& message)
|
||||
MethodReply ObjectProxy::callMethod(const MethodCall& message)
|
||||
{
|
||||
return message.send();
|
||||
}
|
||||
@@ -138,7 +138,7 @@ void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& connectio
|
||||
|
||||
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eSignal);
|
||||
Signal message(sdbusMessage);
|
||||
|
||||
auto* object = static_cast<ObjectProxy*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
|
||||
+2
-2
@@ -52,8 +52,8 @@ namespace internal {
|
||||
, std::string objectPath );
|
||||
~ObjectProxy();
|
||||
|
||||
Message createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
Message callMethod(const Message& message) override;
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
MethodReply callMethod(const MethodCall& message) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
|
||||
Reference in New Issue
Block a user