forked from Kistler-Group/sdbus-cpp
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
e7c78460cf | |||
f5da0dabcb | |||
c9ef1849cd | |||
d154022205 | |||
94fd3c88d8 | |||
a919058d13 | |||
08945acbc4 |
@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(sdbus-c++ VERSION 0.5.1 LANGUAGES C CXX)
|
||||
project(sdbus-c++ VERSION 0.5.3 LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
@ -57,7 +57,6 @@ set(SDBUSCPP_PUBLIC_HDRS
|
||||
${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/Message.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/MethodResult.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/MethodResult.inl
|
||||
${SDBUSCPP_INCLUDE_DIR}/Types.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/Flags.h
|
||||
|
@ -65,3 +65,11 @@ v0.5.1
|
||||
- Rename sdbus-c++ stub code generator executable
|
||||
- Introduce generation of doxygen documentation
|
||||
- Improve documentation
|
||||
|
||||
v0.5.2
|
||||
- Simplify and unify basic API level callbacks for both sync and async methods
|
||||
|
||||
v0.5.3
|
||||
- Extend stress tests with dynamic object and proxy creation and destruction from multiple threads
|
||||
- [[Breaking ABI change]] Add getConnection() method to IObject
|
||||
- A few minor fixes
|
||||
|
@ -150,7 +150,7 @@ This is how a simple Concatenator service implemented upon the basic sdbus-c++ A
|
||||
// to emit signals.
|
||||
sdbus::IObject* g_concatenator{};
|
||||
|
||||
void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
|
||||
void concatenate(sdbus::MethodCall call)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
@ -170,8 +170,10 @@ void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Serialize resulting string to the reply
|
||||
// Serialize resulting string to the reply and send the reply to the caller
|
||||
auto reply = call.createReply();
|
||||
reply << result;
|
||||
reply.send();
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
@ -205,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
We establish a D-Bus sytem connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
|
||||
|
||||
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall& call, sdbus::MethodReply& reply)`. The first parameter `call` is the incoming method call message. We need to deserialize input arguments from it. When we can invoke the logic and get the results. Then we serialize the results to the second parameter, the pre-constructed `reply` message. The reply is then sent automatically by sdbus-c++. We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
|
||||
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
|
||||
|
||||
Please note that we can create and destroy D-Bus objects on a connection dynamically, at any time during runtime, even while there is an active event loop upon the connection. So managing D-Bus objects' lifecycle (creating, exporting and destroying D-Bus objects) is completely thread-safe.
|
||||
|
||||
@ -709,12 +711,12 @@ Asynchronous server-side methods
|
||||
|
||||
So far in our tutorial, we have only considered simple server methods that are executed in a synchronous way. Sometimes the method call may take longer, however, and we don't want to block (potentially starve) other clients (whose requests may take relative short time). The solution is to execute the D-Bus methods asynchronously, and return the control quickly back to the D-Bus dispatching thread. sdbus-c++ provides API supporting async methods, and gives users the freedom to come up with their own concrete implementation mechanics (one worker thread? thread pool? ...).
|
||||
|
||||
### Lower-level API
|
||||
### Using basic sdbus-c++ API
|
||||
|
||||
Considering the Concatenator example based on lower-level API, if we wanted to write `concatenate` method in an asynchronous way, you only have to adapt method signature and its body (registering the method and all the other stuff stays the same):
|
||||
This is how the concatenate method would look like if wrote it as an asynchronous D-Bus method using the basic, lower-level API of sdbus-c++:
|
||||
|
||||
```c++
|
||||
void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
|
||||
void concatenate(sdbus::MethodCall call)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
@ -725,24 +727,27 @@ void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
|
||||
call >> separator;
|
||||
|
||||
// Launch a thread for async execution...
|
||||
std::thread([numbers = std::move(numbers), separator = std::move(separator), result = std::move(result)]()
|
||||
std::thread([numbers = std::move(numbers), separator = std::move(separator), call = std::move(call)]()
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
{
|
||||
// This will send the error reply message back to the client
|
||||
result.returnError("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
// Let's send the error reply message back to the client
|
||||
auto reply = call.createErrorReply({"org.sdbuscpp.Concatenator.Error", "No numbers provided"});
|
||||
reply.send();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string concatenatedStr;
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
concatenatedStr += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// This will send the reply message back to the client
|
||||
result.returnResults(concatenatedStr);
|
||||
// Let's send the reply message back to the client
|
||||
auto reply = call.createReply();
|
||||
reply << result;
|
||||
reply.send();
|
||||
|
||||
// Emit 'concatenated' signal (creating and emitting signals is thread-safe)
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
@ -753,34 +758,53 @@ void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
|
||||
}
|
||||
```
|
||||
|
||||
Notice these differences as compared to the synchronous version:
|
||||
There are a few slight differences compared to the synchronous version. Notice that we `std::move` the `call` message to the worker thread (btw we might also do input arguments deserialization in the worker thread, we don't have to do it in the current thread and then move input arguments to the worker thread...). We need the `call` message there to create the reply message once we have the (normal or error) result. Creating and sending replies, as well as creating and emitting signals is thread-safe by design. Also notice that, unlike in sync methods, sending back errors cannot be done by throwing `Error`, since we are now in the context of the worker thread, not that of the D-Bus dispatcher thread. Instead, we pass the `Error` object to the `createErrorReply()` method of the call message (this way of sending back errors, in addition to throwing, we can actually use also in classic synchronous D-Bus methods).
|
||||
|
||||
* `MethodCall` message is not given by reference, but it's `std::move`d in. Usually, we either deserialize D-Bus method input arguments synchronously (as in the example above) and then `std::move` them to the worker thread, or we `std::move` (or copy, but moving is faster and idiomatic) the `MethodCall` message to that thread directly and do both the deserialization of input arguments and the logic in that thread.
|
||||
|
||||
* Instead of `MethodReply` message given by reference, there is `MethodResult&&` as a second parameter of the callback. `MethodResult` class is move-only.
|
||||
|
||||
* We shall `std::move` the `MethodResult` to the worker thread, and eventually write D-Bus method results (or method error, respectively) to it via its `returnResults()` method (or `returnError()` method, respectively).
|
||||
|
||||
* Unlike in sync methods, reporting errors cannot be done by throwing `Error`, since the execution takes place out of the context of the D-Bus dispatcher thread. Instead, just pass the error name and message to the `returnError` method of the result holder.
|
||||
|
||||
That's all.
|
||||
Method callback signature is the same in sync and async version. That means sdbus-c++ doesn't care how we execute our D-Bus method. We might very well in run-time decide whether we execute it synchronously, or whether (perhaps in case of longer, more complex calculations) we move the execution to a worker thread.
|
||||
|
||||
### Convenience API
|
||||
|
||||
Callbacks of async methods in convenience sdbus-c++ API also need to take the result object as a parameter. The requirements are:
|
||||
Callbacks of async methods based on convenience sdbus-c++ API have slightly different signature. They take a result object parameter in addition to other input parameters. The requirements are:
|
||||
|
||||
* The result holder is of type `sdbus::Result<Types...>`, where `Types...` is a list of method output argument types.
|
||||
* The result object must be a first physical parameter of the callback taken by value.
|
||||
* The result holder is of type `Result<Types...>&&`, where `Types...` is a list of method output argument types.
|
||||
* The result object must be the first physical parameter of the callback taken by r-value ref. `Result` class template is move-only.
|
||||
* The callback itself is physically a void-returning function.
|
||||
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, copying is a fallback.
|
||||
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying.
|
||||
|
||||
For example, we would have to change the concatenate callback signature from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`.
|
||||
So the concatenate callback signature would change from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`:
|
||||
|
||||
The `Result` class template has effectively the same API as the `MethodResult` class mentioned in the above example (it inherits from `MethodResult`), so you use it in the very same way to send the results or an error back to the client.
|
||||
```c++
|
||||
void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator) override
|
||||
{
|
||||
// Launch a thread for async execution...
|
||||
std::thread([this, methodResult = std::move(result), numbers = std::move(numbers), separator = std::move(separator)]()
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
{
|
||||
// Let's send the error reply message back to the client
|
||||
methodResult.returnError({"org.sdbuscpp.Concatenator.Error", "No numbers provided"});
|
||||
return;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Let's send the reply message back to the client
|
||||
methodResult.returnReply(result);
|
||||
|
||||
// Emit the 'concatenated' signal with the resulting string
|
||||
this->concatenated(result);
|
||||
}).detach();
|
||||
}
|
||||
```
|
||||
|
||||
Nothing else has to be changed. The registration of the method callback (`implementedAs`) and all the mechanics around remains completely the same.
|
||||
The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnReply()`) or an error (`returnError()`) which we want to send back to the client.
|
||||
|
||||
Note: Async D-Bus method doesn't necessarily mean we always have to delegate the work to a different thread and immediately return. We can very well execute the work and return the results (via `returnResults()`) or an error (via `return Error()`) synchronously -- i.e. directly in this thread. sdbus-c++ doesn't care, it supports both approaches. This has the benefit that we can decide at run-time, per each method call, whether we execute it synchronously or (in case of complex operation, for example) execute it asynchronously by moving the work to a worker thread.
|
||||
Registraion (`implementedAs()`) doesn't change. Nothing else needs to change.
|
||||
|
||||
### Marking server-side async methods in the IDL
|
||||
|
||||
|
@ -65,8 +65,7 @@ namespace sdbus {
|
||||
std::string interfaceName_;
|
||||
std::string inputSignature_;
|
||||
std::string outputSignature_;
|
||||
method_callback syncCallback_;
|
||||
async_method_callback asyncCallback_;
|
||||
method_callback methodCallback_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
@ -55,6 +55,7 @@ namespace sdbus {
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
|
||||
|
||||
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
@ -65,12 +66,7 @@ namespace sdbus {
|
||||
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
if (syncCallback_)
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(syncCallback_));
|
||||
else if(asyncCallback_)
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(asyncCallback_));
|
||||
else
|
||||
SDBUS_THROW_ERROR("Method handler not specified when registering a DBus method", EINVAL);
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
|
||||
}
|
||||
*/
|
||||
|
||||
@ -86,14 +82,14 @@ namespace sdbus {
|
||||
{
|
||||
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
syncCallback_ = [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodReply& reply)
|
||||
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple
|
||||
msg >> inputArgs;
|
||||
call >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
// For callbacks returning a non-void value, `apply' also returns that value.
|
||||
@ -102,7 +98,9 @@ namespace sdbus {
|
||||
|
||||
// The return value is stored to the reply message.
|
||||
// In case of void functions, ret is an empty tuple and thus nothing is stored.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
};
|
||||
|
||||
return *this;
|
||||
@ -113,17 +111,17 @@ 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)
|
||||
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple.
|
||||
msg >> inputArgs;
|
||||
call >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, std::move(result), std::move(inputArgs)); // TODO: Use std::apply when switching to full C++17 support
|
||||
sdbus::apply(callback, typename function_traits<_Function>::async_result_t{std::move(call)}, std::move(inputArgs));
|
||||
};
|
||||
|
||||
return *this;
|
||||
|
@ -76,30 +76,6 @@ namespace sdbus {
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] asyncMethodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
*
|
||||
* This overload register a method callback that will have a freedom to execute
|
||||
* its body in asynchronous contexts, and send the results from those contexts.
|
||||
* This can help in e.g. long operations, which then don't block the D-Bus processing
|
||||
* loop thread.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
*
|
||||
@ -197,6 +173,13 @@ namespace sdbus {
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Signal& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the object
|
||||
*
|
||||
* @return Reference to the D-Bus connection
|
||||
*/
|
||||
virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
|
@ -74,7 +74,7 @@ namespace sdbus {
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class Interfaces
|
||||
: private ObjectHolder<IObject>
|
||||
: protected ObjectHolder<IObject>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
@ -96,7 +96,7 @@ namespace sdbus {
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class ProxyInterfaces
|
||||
: private ObjectHolder<IObjectProxy>
|
||||
: protected ObjectHolder<IObjectProxy>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
|
@ -31,43 +31,11 @@
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
class Object;
|
||||
}
|
||||
class Error;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class MethodResult
|
||||
*
|
||||
* Represents result of an asynchronous server-side method.
|
||||
* An instance is provided to the method and shall be set
|
||||
* by the method to either method return value or an error.
|
||||
*
|
||||
***********************************************/
|
||||
class MethodResult
|
||||
{
|
||||
protected:
|
||||
friend sdbus::internal::Object;
|
||||
|
||||
MethodResult() = default;
|
||||
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:
|
||||
MethodCall call_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Result
|
||||
*
|
||||
@ -77,18 +45,52 @@ namespace sdbus {
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Results>
|
||||
class Result : protected MethodResult
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result() = default;
|
||||
Result(MethodResult&& result);
|
||||
Result(MethodCall call);
|
||||
|
||||
Result(const Result&) = delete;
|
||||
Result& operator=(const Result&) = delete;
|
||||
|
||||
Result(Result&& other) = default;
|
||||
Result& operator=(Result&& other) = default;
|
||||
|
||||
void returnResults(const _Results&... results) const;
|
||||
void returnError(const Error& error) const;
|
||||
|
||||
private:
|
||||
MethodCall call_;
|
||||
};
|
||||
|
||||
template <typename... _Results>
|
||||
inline Result<_Results...>::Result(MethodCall call)
|
||||
: call_(std::move(call))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::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();
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnError(const Error& error) const
|
||||
{
|
||||
auto reply = call_.createErrorReply(error);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/MethodResult.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_METHODRESULT_H_ */
|
||||
|
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* (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_ */
|
@ -44,15 +44,13 @@ namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class MethodResult;
|
||||
template <typename... _Results> class Result;
|
||||
class Error;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
|
||||
using async_method_callback = std::function<void(MethodCall msg, MethodResult&& result)>;
|
||||
using method_callback = std::function<void(MethodCall msg)>;
|
||||
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)>;
|
||||
@ -381,6 +379,7 @@ namespace sdbus {
|
||||
: public function_traits_base<std::tuple<_Results...>, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = true;
|
||||
using async_result_t = Result<_Results...>;
|
||||
};
|
||||
|
||||
template <typename... _Args, typename... _Results>
|
||||
@ -388,6 +387,7 @@ namespace sdbus {
|
||||
: public function_traits_base<std::tuple<_Results...>, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = true;
|
||||
using async_result_t = Result<_Results...>;
|
||||
};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
@ -504,9 +504,9 @@ namespace sdbus {
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, MethodResult&& r
|
||||
, Result<_Args...>&& r
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
@ -558,8 +558,8 @@ namespace sdbus {
|
||||
|
||||
// Convert tuple `t' of values into a list of arguments
|
||||
// and invoke function `f' with those arguments.
|
||||
template <class _Function, class _Tuple>
|
||||
constexpr decltype(auto) apply(_Function&& f, MethodResult&& r, _Tuple&& t)
|
||||
template <class _Function, class _Tuple, typename... _Args>
|
||||
constexpr decltype(auto) apply(_Function&& f, Result<_Args...>&& r, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, std::move(r)
|
||||
|
@ -39,12 +39,13 @@ MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodN
|
||||
}
|
||||
|
||||
MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the method if MethodRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
|
||||
|
||||
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
@ -55,12 +56,7 @@ MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destruct
|
||||
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
if (syncCallback_)
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(syncCallback_), flags_);
|
||||
else if(asyncCallback_)
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(asyncCallback_), flags_);
|
||||
else
|
||||
SDBUS_THROW_ERROR("Method handler not specified when registering a DBus method", EINVAL);
|
||||
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
|
||||
}
|
||||
|
||||
SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
|
@ -51,37 +51,8 @@ void Object::registerMethod( const std::string& interfaceName
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto syncCallback = [callback = std::move(methodCallback)](MethodCall& msg)
|
||||
{
|
||||
auto reply = msg.createReply();
|
||||
callback(msg, reply);
|
||||
reply.send();
|
||||
};
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(syncCallback), flags};
|
||||
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
|
||||
, Flags flags )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto asyncCallback = [callback = std::move(asyncMethodCallback)](MethodCall& msg)
|
||||
{
|
||||
MethodResult result{msg};
|
||||
callback(std::move(msg), std::move(result));
|
||||
};
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(asyncCallback), flags};
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
@ -159,6 +130,11 @@ void Object::emitSignal(const sdbus::Signal& message)
|
||||
message.send();
|
||||
}
|
||||
|
||||
sdbus::IConnection& Object::getConnection() const
|
||||
{
|
||||
return dynamic_cast<sdbus::IConnection&>(connection_);
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
@ -245,7 +221,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
|
||||
|
||||
try
|
||||
{
|
||||
callback(message);
|
||||
callback(std::move(message));
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
|
11
src/Object.h
11
src/Object.h
@ -52,13 +52,6 @@ namespace internal {
|
||||
, method_callback methodCallback
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature
|
||||
@ -84,6 +77,8 @@ namespace internal {
|
||||
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
|
||||
private:
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
@ -93,7 +88,7 @@ namespace internal {
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
std::function<void(MethodCall&)> callback_;
|
||||
method_callback callback_;
|
||||
Flags flags_;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
|
@ -64,6 +64,8 @@ set(STRESSTESTS_SRCS
|
||||
${STRESSTESTS_SOURCE_DIR}/sdbus-c++-stress-tests.cpp
|
||||
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-proxy.h
|
||||
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-proxy.h
|
||||
${STRESSTESTS_SOURCE_DIR}/concatenator-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/concatenator-proxy.h)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__celsius_thermometer_adaptor_h__adaptor__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
namespace celsius {
|
||||
|
||||
class thermometer_adaptor
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__celsius_thermometer_proxy_h__proxy__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
namespace celsius {
|
||||
|
||||
class thermometer_proxy
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_adaptor_h__adaptor__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
|
||||
class concatenator_adaptor
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_proxy_h__proxy__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
|
||||
class concatenator_proxy
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__fahrenheit_thermometer_adaptor_h__adaptor__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
namespace fahrenheit {
|
||||
|
||||
class thermometer_adaptor
|
||||
@ -36,4 +36,33 @@ private:
|
||||
|
||||
}}}} // namespaces
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstests {
|
||||
namespace fahrenheit {
|
||||
namespace thermometer {
|
||||
|
||||
class factory_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
|
||||
|
||||
protected:
|
||||
factory_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.registerMethod("createDelegateObject").onInterface(interfaceName).implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
|
||||
object_.registerMethod("destroyDelegateObject").onInterface(interfaceName).implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
|
||||
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}}}}} // namespaces
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__fahrenheit_thermometer_proxy_h__proxy__H__
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstest {
|
||||
namespace stresstests {
|
||||
namespace fahrenheit {
|
||||
|
||||
class thermometer_proxy
|
||||
@ -40,4 +40,40 @@ private:
|
||||
|
||||
}}}} // namespaces
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace stresstests {
|
||||
namespace fahrenheit {
|
||||
namespace thermometer {
|
||||
|
||||
class factory_proxy
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
|
||||
|
||||
protected:
|
||||
factory_proxy(sdbus::IObjectProxy& object)
|
||||
: object_(object)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
sdbus::ObjectPath createDelegateObject()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
object_.callMethod("createDelegateObject").onInterface(interfaceName).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void destroyDelegateObject(const sdbus::ObjectPath& delegate)
|
||||
{
|
||||
object_.callMethod("destroyDelegateObject").onInterface(interfaceName).withArguments(delegate).dontExpectReply();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
};
|
||||
|
||||
}}}}} // namespaces
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/stresstest/celsius/thermometer">
|
||||
<interface name="org.sdbuscpp.stresstest.celsius.thermometer">
|
||||
<node name="/org/sdbuscpp/stresstests/celsius/thermometer">
|
||||
<interface name="org.sdbuscpp.stresstests.celsius.thermometer">
|
||||
<method name="getCurrentTemperature">
|
||||
<arg type="u" name="result" direction="out" />
|
||||
</method>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/stresstest/concatenator">
|
||||
<interface name="org.sdbuscpp.stresstest.concatenator">
|
||||
<node name="/org/sdbuscpp/stresstests/concatenator">
|
||||
<interface name="org.sdbuscpp.stresstests.concatenator">
|
||||
<method name="concatenate">
|
||||
<arg type="a{sv}" name="params" direction="in" />
|
||||
<arg type="s" name="result" direction="out" />
|
||||
|
@ -1,9 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/stresstest/fahrenheit/thermometer">
|
||||
<interface name="org.sdbuscpp.stresstest.fahrenheit.thermometer">
|
||||
<node name="/org/sdbuscpp/stresstests/fahrenheit/thermometer">
|
||||
<interface name="org.sdbuscpp.stresstests.fahrenheit.thermometer">
|
||||
<method name="getCurrentTemperature">
|
||||
<arg type="u" name="result" direction="out" />
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.sdbuscpp.stresstests.fahrenheit.thermometer.factory">
|
||||
<method name="createDelegateObject">
|
||||
<arg type="o" name="delegate" direction="out" />
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
|
||||
</method>
|
||||
<method name="destroyDelegateObject">
|
||||
<arg type="o" name="delegate" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
|
@ -46,16 +46,16 @@
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"
|
||||
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"
|
||||
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"
|
||||
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"
|
||||
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"
|
||||
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"s
|
||||
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"s
|
||||
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"s
|
||||
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
|
||||
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
|
||||
|
||||
class CelsiusThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::celsius::thermometer_adaptor>
|
||||
class CelsiusThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
|
||||
{
|
||||
public:
|
||||
using sdbus::Interfaces<org::sdbuscpp::stresstest::celsius::thermometer_adaptor>::Interfaces;
|
||||
using sdbus::Interfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>::Interfaces;
|
||||
|
||||
protected:
|
||||
virtual uint32_t getCurrentTemperature() override
|
||||
@ -67,19 +67,74 @@ private:
|
||||
uint32_t m_currentTemperature{};
|
||||
};
|
||||
|
||||
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::celsius::thermometer_proxy>
|
||||
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::celsius::thermometer_proxy>::ProxyInterfaces;
|
||||
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>::ProxyInterfaces;
|
||||
};
|
||||
|
||||
class FahrenheitThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_adaptor>
|
||||
class FahrenheitThermometerAdaptor : public sdbus::Interfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
|
||||
{
|
||||
public:
|
||||
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
|
||||
: sdbus::Interfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_adaptor>(connection, std::move(objectPath))
|
||||
: sdbus::Interfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >(connection, std::move(objectPath))
|
||||
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
|
||||
{
|
||||
unsigned int workers = std::thread::hardware_concurrency();
|
||||
if (workers < 4)
|
||||
workers = 4;
|
||||
|
||||
for (unsigned int i = 0; i < workers; ++i)
|
||||
workers_.emplace_back([this]()
|
||||
{
|
||||
//std::cout << "Created FTA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
|
||||
|
||||
while(!exit_)
|
||||
{
|
||||
// Pop a work item from the queue
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
|
||||
if (exit_)
|
||||
break;
|
||||
auto request = std::move(requests_.front());
|
||||
requests_.pop();
|
||||
lock.unlock();
|
||||
|
||||
// Either create or destroy a delegate object
|
||||
if (request.delegateObjectPath.empty())
|
||||
{
|
||||
// Create new delegate object
|
||||
auto& connection = getObject().getConnection();
|
||||
sdbus::ObjectPath newObjectPath = FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr);
|
||||
|
||||
// Here we are testing dynamic creation of a D-Bus object in an async way
|
||||
auto adaptor = std::make_unique<FahrenheitThermometerAdaptor>(connection, newObjectPath);
|
||||
|
||||
std::unique_lock<std::mutex> lock{childrenMutex_};
|
||||
children_.emplace(newObjectPath, std::move(adaptor));
|
||||
lock.unlock();
|
||||
|
||||
request.result.returnResults(newObjectPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destroy existing delegate object
|
||||
// Here we are testing dynamic removal of a D-Bus object in an async way
|
||||
std::lock_guard<std::mutex> lock{childrenMutex_};
|
||||
children_.erase(request.delegateObjectPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~FahrenheitThermometerAdaptor()
|
||||
{
|
||||
exit_ = true;
|
||||
cond_.notify_all();
|
||||
for (auto& worker : workers_)
|
||||
worker.join();
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -89,21 +144,56 @@ protected:
|
||||
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
|
||||
}
|
||||
|
||||
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
|
||||
{
|
||||
static size_t objectCounter{};
|
||||
objectCounter++;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
|
||||
lock.unlock();
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
virtual void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
requests_.push(WorkItem{0, std::move(delegate), {}});
|
||||
lock.unlock();
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
CelsiusThermometerProxy celsiusProxy_;
|
||||
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
|
||||
std::mutex childrenMutex_;
|
||||
|
||||
struct WorkItem
|
||||
{
|
||||
size_t objectNr;
|
||||
sdbus::ObjectPath delegateObjectPath;
|
||||
sdbus::Result<sdbus::ObjectPath> result;
|
||||
};
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_;
|
||||
std::queue<WorkItem> requests_;
|
||||
std::vector<std::thread> workers_;
|
||||
std::atomic<bool> exit_{};
|
||||
};
|
||||
|
||||
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_proxy>
|
||||
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_proxy>::ProxyInterfaces;
|
||||
using sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >::ProxyInterfaces;
|
||||
};
|
||||
|
||||
class ConcatenatorAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::concatenator_adaptor>
|
||||
class ConcatenatorAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
|
||||
{
|
||||
public:
|
||||
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
|
||||
: sdbus::Interfaces<org::sdbuscpp::stresstest::concatenator_adaptor>(connection, std::move(objectPath))
|
||||
: sdbus::Interfaces<org::sdbuscpp::stresstests::concatenator_adaptor>(connection, std::move(objectPath))
|
||||
{
|
||||
unsigned int workers = std::thread::hardware_concurrency();
|
||||
if (workers < 4)
|
||||
@ -112,7 +202,7 @@ public:
|
||||
for (unsigned int i = 0; i < workers; ++i)
|
||||
workers_.emplace_back([this]()
|
||||
{
|
||||
//std::cout << "Created worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
|
||||
//std::cout << "Created CA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
|
||||
|
||||
while(!exit_)
|
||||
{
|
||||
@ -167,10 +257,10 @@ private:
|
||||
std::atomic<bool> exit_{};
|
||||
};
|
||||
|
||||
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::concatenator_proxy>
|
||||
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::concatenator_proxy>::ProxyInterfaces;
|
||||
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>::ProxyInterfaces;
|
||||
|
||||
private:
|
||||
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) override
|
||||
@ -204,8 +294,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
std::atomic<uint32_t> repliesReceived_;
|
||||
std::atomic<uint32_t> signalsReceived_;
|
||||
std::atomic<uint32_t> repliesReceived_{};
|
||||
std::atomic<uint32_t> signalsReceived_{};
|
||||
};
|
||||
|
||||
//-----------------------------------------
|
||||
@ -258,8 +348,8 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
// Make sure the system is catching up with our async requests,
|
||||
// otherwise sleep a bit to slow down flooding the server.
|
||||
assert(localCounter >= concatenator.repliesReceived_);
|
||||
while ((localCounter - concatenator.repliesReceived_) > 20 && !stopClients)
|
||||
std::this_thread::sleep_for(2ms);
|
||||
while ((localCounter - concatenator.repliesReceived_) > 40 && !stopClients)
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
// Update statistics
|
||||
concatenationCallsMade = localCounter;
|
||||
@ -271,6 +361,11 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
|
||||
std::thread thermometerThread([&]()
|
||||
{
|
||||
// Here we continuously remotely call getCurrentTemperature(). We have one proxy object,
|
||||
// first we use it's factory interface to create another proxy object, call getCurrentTemperature()
|
||||
// on that one, and then destroy that proxy object. All that continously in a loop.
|
||||
// This tests dynamic creation and destruction of remote D-Bus objects and local object proxies.
|
||||
|
||||
FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
|
||||
uint32_t localCounter{};
|
||||
uint32_t previousTemperature{};
|
||||
@ -278,13 +373,20 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
while (!stopClients)
|
||||
{
|
||||
localCounter++;
|
||||
auto temperature = thermometer.getCurrentTemperature();
|
||||
|
||||
auto newObjectPath = thermometer.createDelegateObject();
|
||||
FahrenheitThermometerProxy proxy{con, SERVICE_1_BUS_NAME, newObjectPath};
|
||||
|
||||
auto temperature = proxy.getCurrentTemperature();
|
||||
assert(temperature >= previousTemperature); // The temperature shall rise continually
|
||||
previousTemperature = temperature;
|
||||
std::this_thread::sleep_for(5ms);
|
||||
//std::this_thread::sleep_for(1ms);
|
||||
|
||||
if ((localCounter % 10) == 0)
|
||||
thermometerCallsMade = localCounter;
|
||||
|
||||
proxy.~FahrenheitThermometerProxy();
|
||||
thermometer.destroyDelegateObject(newObjectPath);
|
||||
}
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user